Author: ajaquith
Date: Sun Aug 3 05:15:57 2008
New Revision: 682143
URL: http://svn.apache.org/viewvc?rev=682143&view=rev
Log:
Massive re-factoring to make Stripes less invasive.
Added:
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/acl/package.html
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/package.html
Removed:
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PolicyLoader.java
Modified:
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentManager.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentServlet.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/AuthenticationManager.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/AuthorizationManager.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/Authorizer.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/GroupPrincipal.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/NoSuchPrincipalException.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PrincipalComparator.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/SecurityVerifier.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/SessionMonitor.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/UserManager.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/WikiPrincipal.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/WikiSecurityException.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/acl/Acl.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/acl/AclEntry.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/acl/AclEntryImpl.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/acl/AclImpl.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/acl/AclManager.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/acl/DefaultAclManager.java
incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/acl/UnresolvedPrincipal.java
Modified: incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentManager.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentManager.java?rev=682143&r1=682142&r2=682143&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentManager.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentManager.java Sun Aug 3 05:15:57 2008
@@ -26,6 +26,7 @@
import java.io.InputStream;
import java.util.*;
+import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import com.ecyrd.jspwiki.*;
@@ -121,8 +122,8 @@
//
try
{
- Class providerclass = ClassUtil.findClass( "com.ecyrd.jspwiki.providers",
- classname );
+ Class<?> providerclass = ClassUtil.findClass( "com.ecyrd.jspwiki.providers",
+ classname );
m_provider = (WikiAttachmentProvider)providerclass.newInstance();
@@ -307,22 +308,23 @@
*/
// FIXME: This API should be changed to return a List.
- public Collection<Attachment> listAttachments( WikiPage wikipage )
+ @SuppressWarnings("unchecked")
+ public Collection listAttachments( WikiPage wikipage )
throws ProviderException
{
if( m_provider == null )
{
- return new ArrayList<Attachment>();
+ return new ArrayList();
}
- Collection<Attachment> atts = m_provider.listAttachments( wikipage );
+ Collection atts = m_provider.listAttachments( wikipage );
//
// This is just a sanity check; all of our providers return a Collection.
//
if( atts instanceof List )
{
- Collections.sort( (List<Attachment>)atts );
+ Collections.sort( (List) atts );
}
return atts;
@@ -534,7 +536,7 @@
* return an empty collection.
* @throws ProviderException If something went wrong with the backend
*/
- public Collection<Attachment> getAllAttachments()
+ public Collection getAllAttachments()
throws ProviderException
{
if( attachmentsEnabled() )
@@ -587,4 +589,53 @@
m_engine.getReferenceManager().clearPageEntries( att.getName() );
}
+
+ /**
+ * Validates the filename and makes sure it is legal. It trims and splits
+ * and replaces bad characters.
+ *
+ * @param filename
+ * @return A validated name with annoying characters replaced.
+ * @throws WikiException If the filename is not legal (e.g. empty)
+ */
+ static String validateFileName( String filename )
+ throws WikiException
+ {
+ if( filename == null || filename.trim().length() == 0 )
+ {
+ AttachmentServlet.log.error("Empty file name given.");
+
+ throw new WikiException("Empty file name given.");
+ }
+
+ //
+ // Should help with IE 5.22 on OSX
+ //
+ filename = filename.trim();
+
+ // If file name ends with .jsp, the user is being naughty!
+ if ( filename.endsWith( ".jsp" ) || filename.endsWith( ".JSP" ) )
+ {
+ AttachmentServlet.log.error( "Illegal file name." );
+
+ throw new WikiException( "Illegal file name." );
+ }
+
+ //
+ // Some browser send the full path info with the filename, so we need
+ // to remove it here by simply splitting along slashes and then taking the path.
+ //
+
+ String[] splitpath = filename.split( "[/\\\\]" );
+ filename = splitpath[splitpath.length-1];
+
+ //
+ // Remove any characters that might be a problem. Most
+ // importantly - characters that might stop processing
+ // of the URL.
+ //
+ filename = StringUtils.replaceChars( filename, "#?\"'", "____" );
+
+ return filename;
+ }
}
Modified: incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentServlet.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentServlet.java?rev=682143&r1=682142&r2=682143&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentServlet.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentServlet.java Sun Aug 3 05:15:57 2008
@@ -1,21 +1,22 @@
/*
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
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
*/
package com.ecyrd.jspwiki.attachment;
@@ -26,7 +27,7 @@
import java.net.SocketException;
import java.security.Permission;
import java.security.Principal;
-import java.util.Enumeration;
+import java.util.List;
import java.util.Properties;
import javax.servlet.ServletConfig;
@@ -35,16 +36,15 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import net.iamvegan.multipartrequest.HttpServletMultipartRequest;
-import net.iamvegan.multipartrequest.MultipartFile;
-import net.iamvegan.multipartrequest.ProgressListener;
-
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileItemFactory;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.ProgressListener;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;
import com.ecyrd.jspwiki.*;
-import com.ecyrd.jspwiki.action.AttachActionBean;
-import com.ecyrd.jspwiki.action.UploadActionBean;
import com.ecyrd.jspwiki.auth.AuthorizationManager;
import com.ecyrd.jspwiki.auth.permissions.PermissionFactory;
import com.ecyrd.jspwiki.dav.AttachmentDavProvider;
@@ -69,7 +69,6 @@
* This servlet is also capable of managing dynamically created attachments.
*
* @author Erik Bunn
- * @author Janne Jalkanen
*
* @since 1.9.45.
*/
@@ -229,15 +228,7 @@
public void doGet( HttpServletRequest req, HttpServletResponse res )
throws IOException, ServletException
{
- WikiContext context;
- try
- {
- context = (WikiContext)m_engine.getWikiActionBeanFactory().newActionBean( req, res, AttachActionBean.class );
- }
- catch ( WikiException e )
- {
- throw new ServletException( e.getMessage() );
- }
+ WikiContext context = m_engine.createContext( req, WikiContext.ATTACH );
String version = req.getParameter( HDR_VERSION );
String nextPage = req.getParameter( "nextpage" );
@@ -478,7 +469,7 @@
{
try
{
- String nextPage = upload( req, res );
+ String nextPage = upload( req );
req.getSession().removeAttribute("msg");
res.sendRedirect( nextPage );
}
@@ -498,6 +489,8 @@
public void doPut( HttpServletRequest req, HttpServletResponse res )
throws IOException, ServletException
{
+ String errorPage = m_engine.getURL( WikiContext.ERROR, "", null, false ); // If something bad happened, Upload should be able to take care of most stuff
+
String p = new String(req.getPathInfo().getBytes("ISO-8859-1"), "UTF-8");
DavPath path = new DavPath( p );
@@ -505,21 +498,11 @@
{
InputStream data = req.getInputStream();
- WikiContext context;
- String errorPage; // If something bad happened, Upload should be able to take care of most stuff
-
- try
- {
- context = (WikiContext)m_engine.getWikiActionBeanFactory().newActionBean( req, res, UploadActionBean .class );
- }
- catch ( WikiException e )
- {
- throw new ServletException( e.getMessage() );
- }
+ WikiContext context = (WikiContext)m_engine.createContext( req, WikiContext.UPLOAD );
String wikipage = path.get( 0 );
- errorPage = context.getContext().getURL( UploadActionBean.class,
+ errorPage = context.getURL( WikiContext.UPLOAD,
wikipage );
String changeNote = null; // FIXME: Does not quite work
@@ -576,77 +559,104 @@
* @return The page to which we should go next.
* @throws RedirectException If there's an error and a redirection is needed
* @throws IOException If upload fails
+ * @throws FileUploadException
*/
- protected String upload( HttpServletRequest req, HttpServletResponse res )
+ @SuppressWarnings("unchecked")
+ protected String upload( HttpServletRequest req )
throws RedirectException,
IOException
{
String msg = "";
String attName = "(unknown)";
- String nextPage;
+ String errorPage = m_engine.getURL( WikiContext.ERROR, "", null, false ); // If something bad happened, Upload should be able to take care of most stuff
+ String nextPage = errorPage;
String progressId = req.getParameter( "progressid" );
+ // Check that we have a file upload request
+ if( !ServletFileUpload.isMultipartContent(req) )
+ {
+ throw new RedirectException( "Not a file upload", errorPage );
+ }
+
try
{
- HttpServletMultipartRequest multi;
-
+ FileItemFactory factory = new DiskFileItemFactory();
+
// Create the context _before_ Multipart operations, otherwise
// strict servlet containers may fail when setting encoding.
- WikiContext context;
- try
- {
- context = (WikiContext)m_engine.getWikiActionBeanFactory().newActionBean( req, res, AttachActionBean.class );
- }
- catch ( WikiException e )
- {
- throw new IOException( e.getMessage() );
- }
+ WikiContext context = (WikiContext)m_engine.createContext( req, WikiContext.ATTACH );
UploadListener pl = new UploadListener();
m_engine.getProgressManager().startProgress( pl, progressId );
- multi = new HttpServletMultipartRequest( req,
- Long.MAX_VALUE,
- HttpServletMultipartRequest.SAVE_TO_TMPDIR,
- HttpServletMultipartRequest.ABORT_ON_MAX_LENGTH,
- "UTF-8",
- pl );
-
- String errorPage = context.getContext().getURL( UploadActionBean.class, context.getPage().getName() );
- nextPage = validateNextPage( multi.getParameter( "nextpage" ), errorPage );
- String wikipage = multi.getParameter( "page" );
- String changeNote = multi.getParameter( "changenote" );
-
- //
- // FIXME: Kludge alert. We must end up with the parent page name,
- // if this is an upload of a new revision
- //
-
- int x = wikipage.indexOf("/");
-
- if( x != -1 ) wikipage = wikipage.substring(0,x);
+ ServletFileUpload upload = new ServletFileUpload(factory);
+ upload.setHeaderEncoding("UTF-8");
+ upload.setFileSizeMax( m_maxSize );
+ upload.setProgressListener( pl );
+ List<FileItem> items = upload.parseRequest( req );
+
+ String wikipage = null;
+ String changeNote = null;
+ FileItem actualFile = null;
+
+ for( FileItem item : items )
+ {
+ if( item.isFormField() )
+ {
+ if( item.getFieldName().equals("page") )
+ {
+ //
+ // FIXME: Kludge alert. We must end up with the parent page name,
+ // if this is an upload of a new revision
+ //
+
+ wikipage = item.getString("UTF-8");
+ int x = wikipage.indexOf("/");
+
+ if( x != -1 ) wikipage = wikipage.substring(0,x);
+ }
+ else if( item.getFieldName().equals("changenote") )
+ {
+ changeNote = item.getString("UTF-8");
+ }
+ else if( item.getFieldName().equals( "nextpage" ) )
+ {
+ nextPage = validateNextPage( item.getString("UTF-8"), errorPage );
+ }
+ }
+ else
+ {
+ actualFile = item;
+ }
+ }
+ if( actualFile == null )
+ throw new RedirectException( "Broken file upload", errorPage );
+
//
- // Go through all files being uploaded.
- //
- Enumeration files = multi.getFileParameterNames();
- long fileSize = 0L;
- while( files.hasMoreElements() )
- {
- String part = (String) files.nextElement();
- MultipartFile multiFile = multi.getFileParameter(part);
- fileSize += multiFile.getSize();
- InputStream in = multiFile.getInputStream();
-
- String filename = multiFile.getName();
-
+ // FIXME: Unfortunately, with Apache fileupload we will get the form fields in
+ // order. This means that we have to gather all the metadata from the
+ // request prior to actually touching the uploaded file itself. This
+ // is because the changenote appears after the file upload box, and we
+ // would not have this information when uploading. This also means
+ // that with current structure we can only support a single file upload
+ // at a time.
+ //
+ String filename = actualFile.getName();
+ long fileSize = actualFile.getSize();
+ InputStream in = actualFile.getInputStream();
+
+ try
+ {
executeUpload( context, in, filename, nextPage, wikipage, changeNote, fileSize );
}
+ finally
+ {
+ in.close();
+ }
- // Inform the JSP page of which file we are handling:
- // req.setAttribute( ATTR_ATTACHMENT, wikiname );
}
catch( ProviderException e )
{
@@ -664,6 +674,15 @@
throw e;
}
+ catch (FileUploadException e)
+ {
+ // Show the submit page again, but with a bit more
+ // intimidating output.
+ msg = "Upload failure: " + e.getMessage();
+ log.warn( msg + " (attachment: " + attName + ")", e );
+
+ throw new IOException( msg );
+ }
finally
{
m_engine.getProgressManager().stopProgress( progressId );
@@ -674,7 +693,6 @@
return nextPage;
}
-
/**
*
* @param context the wiki context
@@ -699,6 +717,16 @@
{
boolean created = false;
+ try
+ {
+ filename = AttachmentManager.validateFileName( filename );
+ }
+ catch( WikiException e )
+ {
+ log.error( "Illegal filename given: "+e.getMessage() );
+ throw new RedirectException( e.getMessage(), errorPage );
+ }
+
//
// FIXME: This has the unfortunate side effect that it will receive the
// contents. But we can't figure out the page to redirect to
@@ -725,26 +753,6 @@
AttachmentManager mgr = m_engine.getAttachmentManager();
- if( filename == null || filename.trim().length() == 0 )
- {
- log.error("Empty file name given.");
-
- throw new RedirectException("Empty file name given.",
- errorPage);
- }
-
- //
- // Should help with IE 5.22 on OSX
- //
- filename = filename.trim();
-
- //
- // Remove any characters that might be a problem. Most
- // importantly - characters that might stop processing
- // of the URL.
- //
- filename = StringUtils.replaceChars( filename, "#?\"'", "____" );
-
log.debug("file="+filename);
if( data == null )
@@ -812,7 +820,7 @@
*
* @author Janne Jalkanen
*/
- private class UploadListener
+ private static class UploadListener
extends ProgressItem
implements ProgressListener
{
Modified: incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/AuthenticationManager.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/AuthenticationManager.java?rev=682143&r1=682142&r2=682143&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/AuthenticationManager.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/AuthenticationManager.java Sun Aug 3 05:15:57 2008
@@ -1,34 +1,35 @@
/*
- * JSPWiki - a JSP-based WikiWiki clone. Copyright (C) 2001-2003 Janne Jalkanen
- * (Janne.Jalkanen@iki.fi) This program is free software; you can redistribute
- * it and/or modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1 of the
- * License, or (at your option) any later version. This program is distributed
- * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
- * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU Lesser General Public License for more details. You should have
- * received a copy of the GNU Lesser General Public License along with this
- * program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place, Suite 330, Boston, MA 02111-1307 USA
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
*/
package com.ecyrd.jspwiki.auth;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
-import java.security.AccessController;
import java.security.Principal;
-import java.security.PrivilegedAction;
-import java.util.Properties;
+import java.util.*;
+import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.login.AccountExpiredException;
-import javax.security.auth.login.AppConfigurationEntry;
-import javax.security.auth.login.Configuration;
-import javax.security.auth.login.CredentialExpiredException;
-import javax.security.auth.login.FailedLoginException;
-import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@@ -40,83 +41,113 @@
import com.ecyrd.jspwiki.WikiSession;
import com.ecyrd.jspwiki.auth.authorize.Role;
import com.ecyrd.jspwiki.auth.authorize.WebContainerAuthorizer;
-import com.ecyrd.jspwiki.auth.login.CookieAssertionLoginModule;
-import com.ecyrd.jspwiki.auth.login.CookieAuthenticationLoginModule;
-import com.ecyrd.jspwiki.auth.login.WebContainerCallbackHandler;
-import com.ecyrd.jspwiki.auth.login.WikiCallbackHandler;
+import com.ecyrd.jspwiki.auth.login.*;
import com.ecyrd.jspwiki.event.WikiEventListener;
import com.ecyrd.jspwiki.event.WikiEventManager;
import com.ecyrd.jspwiki.event.WikiSecurityEvent;
+import com.ecyrd.jspwiki.util.TimedCounterList;
/**
* Manages authentication activities for a WikiEngine: user login, logout, and
* credential refreshes. This class uses JAAS to determine how users log in.
+ * <p>
+ * The login procedure is protected in addition by a mechanism which prevents
+ * a hacker to try and force-guess passwords by slowing down attempts to log in
+ * into the same account. Every login attempt is recorded, and stored for a while
+ * (currently ten minutes), and each login attempt during that time incurs a penalty
+ * of 2^login attempts milliseconds - that is, 10 login attempts incur a login penalty of 1.024 seconds.
+ * The delay is currently capped to 20 seconds.
+ *
* @author Andrew Jaquith
- * @author Janne Jalkanen
* @author Erik Bunn
* @since 2.3
*/
public final class AuthenticationManager
{
+ /** How many milliseconds the logins are stored before they're cleaned away. */
+ private static final long LASTLOGINS_CLEANUP_TIME = 10*60*1000L; // Ten minutes
+ private static final long MAX_LOGIN_DELAY = 20*1000L; // 20 seconds
+
/** The name of the built-in cookie assertion module */
public static final String COOKIE_MODULE = CookieAssertionLoginModule.class.getName();
/** The name of the built-in cookie authentication module */
public static final String COOKIE_AUTHENTICATION_MODULE = CookieAuthenticationLoginModule.class.getName();
- /** The JAAS application name for the web container authentication stack. */
- public static final String LOGIN_CONTAINER = "JSPWiki-container";
-
- /** The JAAS application name for the JSPWiki custom authentication stack. */
- public static final String LOGIN_CUSTOM = "JSPWiki-custom";
-
/** If this jspwiki.properties property is <code>true</code>, logs the IP address of the editor on saving. */
public static final String PROP_STOREIPADDRESS = "jspwiki.storeIPAddress";
+
+ /** If this jspwiki.properties property is <code>true</code>, allow cookies to be used for authentication. */
+ public static final String PROP_ALLOW_COOKIE_AUTH = "jspwiki.cookieAuthentication";
+
+ /**
+ * This property determines whether we use JSPWiki authentication or not.
+ * Possible values are AUTH_JAAS or AUTH_CONTAINER.
+ * <p>
+ * Setting this is now deprecated - we do not guarantee that it works.
+ *
+ * @deprecated
+ */
+ public static final String PROP_SECURITY = "jspwiki.security";
- protected static final Logger log = Logger.getLogger( AuthenticationManager.class );
+ /** Value specifying that the user wants to use the container-managed security, just like in JSPWiki 2.2. */
+ public static final String SECURITY_OFF = "off";
- /** Was JAAS login config already set before we startd up? */
- protected boolean m_isJaasConfiguredAtStartup = false;
+ /** Value specifying that the user wants to use the built-in JAAS-based system */
+ public static final String SECURITY_JAAS = "jaas";
- /** Static Boolean for lazily-initializing the "allows assertions" flag */
- private static Boolean c_allowsAssertions = null;
+ protected static final Logger log = Logger.getLogger( AuthenticationManager.class );
- /** Static Boolean for lazily-initializing the "allows cookie authentication" flag */
- private static Boolean c_allowsAuthentication = null;
+ /** Prefix for LoginModule options key/value pairs. */
+ protected static final String PREFIX_LOGIN_MODULE_OPTIONS = "jspwiki.loginModule.options.";
- private WikiEngine m_engine = null;
+ /** If this jspwiki.properties property is <code>true</code>, allow cookies to be used to assert identities. */
+ protected static final String PROP_ALLOW_COOKIE_ASSERTIONS = "jspwiki.cookieAssertions";
- /** If true, logs the IP address of the editor */
- private boolean m_storeIPAddress = true;
-
- /** Value specifying that the user wants to use the container-managed security, just like
- * in JSPWiki 2.2.
- */
- public static final String SECURITY_OFF = "off";
+ /** The {@link javax.security.auth.spi.LoginModule} to use for custom authentication. */
+ protected static final String PROP_LOGIN_MODULE = "jspwiki.loginModule.class";
+
+ /** Empty Map passed to JAAS {@link #doJAASLogin(Class, CallbackHandler, Map)} method. */
+ protected static final Map<String,String> EMPTY_MAP = Collections.unmodifiableMap( new HashMap<String,String>() );
+
+ /** Class (of type LoginModule) to use for custom authentication. */
+ protected Class<? extends LoginModule> m_loginModuleClass = UserDatabaseLoginModule.class;
+
+ /** Options passed to {@link javax.security.auth.spi.LoginModule#initialize(Subject, CallbackHandler, Map, Map)};
+ * initialized by {@link #initialize(WikiEngine, Properties)}. */
+ protected Map<String,String> m_loginModuleOptions = new HashMap<String,String>();
/** Just to provide compatibility with the old versions. The same
* as SECURITY_OFF.
*
- * @deprecated
+ * @deprecated use {@link #SECURITY_OFF} instead
*/
protected static final String SECURITY_CONTAINER = "container";
- /** Value specifying that the user wants to use the built-in JAAS-based system */
- public static final String SECURITY_JAAS = "jaas";
+ /** The default {@link javax.security.auth.spi.LoginModule} class name to use for custom authentication. */
+ private static final String DEFAULT_LOGIN_MODULE = "com.ecyrd.jspwiki.auth.login.UserDatabaseLoginModule";
+
+ /** Empty principal set. */
+ private static final Set<Principal> NO_PRINCIPALS = new HashSet<Principal>();
- /**
- * This property determines whether we use JSPWiki authentication or not.
- * Possible values are AUTH_JAAS or AUTH_CONTAINER.
- *
- */
+ /** Static Boolean for lazily-initializing the "allows assertions" flag */
+ private boolean m_allowsCookieAssertions = true;
- public static final String PROP_SECURITY = "jspwiki.security";
- private static final String PROP_JAAS_CONFIG = "java.security.auth.login.config";
- private static final String DEFAULT_JAAS_CONFIG = "jspwiki.jaas";
+ /** Static Boolean for lazily-initializing the "allows cookie authentication" flag */
+ private boolean m_allowsCookieAuthentication = false;
- private static boolean c_useJAAS = true;
+ private WikiEngine m_engine = null;
+
+ /** If true, logs the IP address of the editor */
+ private boolean m_storeIPAddress = true;
+
+ private boolean m_useJAAS = true;
+ /** Keeps a list of the usernames who have attempted a login recently. */
+
+ private TimedCounterList<String> m_lastLoginAttempts = new TimedCounterList<String>();
+
/**
* Creates an AuthenticationManager instance for the given WikiEngine and
* the specified set of properties. All initialization for the modules is
@@ -125,43 +156,39 @@
* @param props the properties used to initialize the wiki engine
* @throws WikiException if the AuthenticationManager cannot be initialized
*/
+ @SuppressWarnings("unchecked")
public final void initialize( WikiEngine engine, Properties props ) throws WikiException
{
m_engine = engine;
m_storeIPAddress = TextUtil.getBooleanProperty( props, PROP_STOREIPADDRESS, m_storeIPAddress );
- m_isJaasConfiguredAtStartup = PolicyLoader.isJaasConfigured();
-
- // Yes, writing to a static field is done here on purpose.
- c_useJAAS = SECURITY_JAAS.equals(props.getProperty( PROP_SECURITY, SECURITY_JAAS ));
-
- if( !c_useJAAS ) return;
- //
- // The rest is JAAS implementation
- //
-
- log.info( "Checking JAAS configuration..." );
-
- if (! m_isJaasConfiguredAtStartup )
+ // Should J2SE policies be used for authorization?
+ m_useJAAS = SECURITY_JAAS.equals(props.getProperty( PROP_SECURITY, SECURITY_JAAS ));
+
+ // Should we allow cookies for assertions? (default: yes)
+ m_allowsCookieAssertions = TextUtil.getBooleanProperty( props,
+ PROP_ALLOW_COOKIE_ASSERTIONS,
+ true );
+
+ // Should we allow cookies for authentication? (default: no)
+ m_allowsCookieAuthentication = TextUtil.getBooleanProperty( props,
+ PROP_ALLOW_COOKIE_AUTH,
+ false );
+
+ // Look up the LoginModule class
+ String loginModuleClassName = TextUtil.getStringProperty( props, PROP_LOGIN_MODULE, DEFAULT_LOGIN_MODULE );
+ try
{
- URL config = findConfigFile( engine, DEFAULT_JAAS_CONFIG );
- log.info("JAAS not configured. Installing default configuration: " + config
- + ". You can set the "+PROP_JAAS_CONFIG+" system property to point to your "
- + "jspwiki.jaas file, or add the entries from jspwiki.jaas to your own "
- + "JAAS configuration file.");
- try
- {
- PolicyLoader.setJaasConfiguration( config );
- }
- catch ( SecurityException e)
- {
- log.error("Could not configure JAAS: " + e.getMessage());
- }
+ m_loginModuleClass = (Class<? extends LoginModule>) Class.forName( loginModuleClassName );
}
- else
+ catch (ClassNotFoundException e)
{
- log.info("JAAS already configured by some other application (leaving it alone...)");
+ e.printStackTrace();
+ throw new WikiException(e.getMessage());
}
+
+ // Initialize the LoginModule options
+ initLoginModuleOptions( props );
}
/**
@@ -175,7 +202,7 @@
*/
public final boolean isContainerAuthenticated()
{
- if( !c_useJAAS ) return true;
+ if( !m_useJAAS ) return true;
try
{
@@ -194,56 +221,122 @@
/**
* <p>Logs in the user by attempting to populate a WikiSession Subject from
- * a web servlet request. This method leverages container-managed authentication.
- * This method logs in the user if the user's status is "unknown" to the
- * WikiSession, or if the Http servlet container's authentication status has
- * changed. This method assumes that the HttpServletRequest is not null; otherwise,
- * an IllegalStateException is thrown. This method is a <em>privileged</em> action;
- * the caller must posess the (name here) permission.</p>
- * <p>If <code>request</code> is <code>null</code>, or the WikiSession
- * cannot be located for this request, this method throws an {@link IllegalStateException}.</p>
- * methods return null
+ * a web servlet request by examining the request
+ * for the presence of container credentials and user cookies. The processing
+ * logic is as follows:
+ * </p>
+ * <ul>
+ * <li>If the WikiSession had previously been unauthenticated, check to see if
+ * user has subsequently authenticated. To be considered "authenticated,"
+ * the request must supply one of the following (in order of preference):
+ * the container <code>userPrincipal</code>, container <code>remoteUser</code>,
+ * or authentication cookie. If the user is authenticated, this method fires event
+ * {@link com.ecyrd.jspwiki.event.WikiSecurityEvent#LOGIN_AUTHENTICATED}
+ * with two parameters: a Principal representing the login principal,
+ * and the current WikiSession. In addition, if the authorizer is of type
+ * WebContainerAuthorizer, this method iterates through the container roles returned by
+ * {@link com.ecyrd.jspwiki.auth.authorize.WebContainerAuthorizer#getRoles()},
+ * tests for membership in each one, and adds those that pass to the Subject's principal set.</li>
+ * <li>If, after checking for authentication, the WikiSession is still Anonymous,
+ * this method next checks to see if the user has "asserted" an identity
+ * by supplying an assertion cookie. If the user is found to be asserted,
+ * this method fires event {@link com.ecyrd.jspwiki.event.WikiSecurityEvent#LOGIN_ASSERTED}
+ * with two parameters: <code>WikiPrincipal(<em>cookievalue</em>)</code>, and
+ * the current WikiSession.</li>
+ * <li>If, after checking for authenticated and asserted status, the WikiSession is
+ * <em>still</em> anonymous, this method fires event
+ * {@link com.ecyrd.jspwiki.event.WikiSecurityEvent#LOGIN_ANONYMOUS} with
+ * two parameters: <code>WikiPrincipal(<em>remoteAddress</em>)</code>,
+ * and the current WikiSession </li>
+ * </ul>
* @param request servlet request for this user
- * @return the result of the login operation: <code>true</code> if the user logged in
- * successfully; <code>false</code> otherwise
- * @throws com.ecyrd.jspwiki.auth.WikiSecurityException if the Authorizer or UserManager cannot be obtained
+ * @return always returns <code>true</code> (because anonymous login, at least, will always succeed)
+ * @throws com.ecyrd.jspwiki.auth.WikiSecurityException if the user cannot be logged in for any reason
* @since 2.3
*/
public final boolean login( HttpServletRequest request ) throws WikiSecurityException
{
- if ( request == null )
+ HttpSession httpSession = request.getSession();
+ WikiSession session = SessionMonitor.getInstance(m_engine).find( httpSession );
+ AuthenticationManager authenticationMgr = m_engine.getAuthenticationManager();
+ AuthorizationManager authorizationMgr = m_engine.getAuthorizationManager();
+ CallbackHandler handler = null;
+ Map<String,String> options = EMPTY_MAP;
+
+ // If user not authenticated, check if container logged them in, or if
+ // there's an authentication cookie
+ if ( !session.isAuthenticated() )
{
- throw new IllegalStateException( "Wiki context's HttpRequest may not be null" );
+ // Create a callback handler
+ try
+ {
+ handler = new WebContainerCallbackHandler( m_engine, request, authorizationMgr.getAuthorizer() );
+ }
+ catch ( WikiSecurityException e )
+ {
+ e.printStackTrace();
+ throw new WikiSecurityException( e.getMessage() );
+ }
+
+ // Execute the container login module, then (if that fails) the cookie auth module
+ Set<Principal> principals = authenticationMgr.doJAASLogin( WebContainerLoginModule.class, handler, options );
+ if ( principals.size() == 0 && authenticationMgr.allowsCookieAuthentication() )
+ {
+ principals = authenticationMgr.doJAASLogin( CookieAuthenticationLoginModule.class, handler, options );
+ }
+
+ // If the container logged the user in successfully, tell the WikiSession (and add all of the Principals)
+ if ( principals.size() > 0 )
+ {
+ fireEvent( WikiSecurityEvent.LOGIN_AUTHENTICATED, getLoginPrincipal( principals ), session );
+ for ( Principal principal : principals )
+ {
+ fireEvent( WikiSecurityEvent.PRINCIPAL_ADD, principal, session );
+ }
+ }
}
- WikiSession wikiSession = WikiSession.getWikiSession( m_engine, request );
- if ( wikiSession == null )
+ // If user still not authenticated, check if assertion cookie was supplied
+ if ( !session.isAuthenticated() && authenticationMgr.allowsCookieAssertions() )
{
- throw new IllegalStateException( "Wiki context's WikiSession may not be null" );
+ // Execute the cookie assertion login module
+ Set<Principal> principals = authenticationMgr.doJAASLogin( CookieAssertionLoginModule.class, handler, options );
+ if ( principals.size() > 0 )
+ {
+ fireEvent( WikiSecurityEvent.LOGIN_ASSERTED, getLoginPrincipal( principals ), session);
+ }
}
- // If using JAAS, try to log in; otherwise logins "always" succeed
- boolean login = true;
- if( c_useJAAS )
+ // If user still anonymous, use the remote address
+ if (session.isAnonymous() )
{
- AuthorizationManager authMgr = m_engine.getAuthorizationManager();
- CallbackHandler handler = new WebContainerCallbackHandler(
- m_engine,
- request,
- authMgr.getAuthorizer() );
- login = doLogin( wikiSession, handler, LOGIN_CONTAINER );
+ Set<Principal> principals = authenticationMgr.doJAASLogin( AnonymousLoginModule.class, handler, options );
+ if ( principals.size() > 0 )
+ {
+ fireEvent( WikiSecurityEvent.LOGIN_ANONYMOUS, getLoginPrincipal( principals ), session );
+ return true;
+ }
}
- return login;
+
+ // If by some unusual turn of events the Anonymous login module doesn't work, login failed!
+ return false;
}
-
+
/**
* Attempts to perform a WikiSession login for the given username/password
- * combination. This is custom authentication.
+ * combination using JSPWiki's custom authentication mode. In order to log in,
+ * the JAAS LoginModule supplied by the WikiEngine property {@link #PROP_LOGIN_MODULE}
+ * will be instantiated, and its
+ * {@link javax.security.auth.spi.LoginModule#initialize(Subject, CallbackHandler, Map, Map)}
+ * method will be invoked. By default, the {@link com.ecyrd.jspwiki.auth.login.UserDatabaseLoginModule}
+ * class will be used. When the LoginModule's <code>initialize</code> method is invoked,
+ * an options Map populated by properties keys prefixed by {@link #PREFIX_LOGIN_MODULE_OPTIONS}
+ * will be passed as a parameter.
* @param session the current wiki session; may not be null.
* @param username The user name. This is a login name, not a WikiName. In
* most cases they are the same, but in some cases, they might
* not be.
- * @param password The password
+ * @param password the password
* @return true, if the username/password is valid
* @throws com.ecyrd.jspwiki.auth.WikiSecurityException if the Authorizer or UserManager cannot be obtained
*/
@@ -255,12 +348,54 @@
return false;
}
+ delayLogin(username);
+
UserManager userMgr = m_engine.getUserManager();
CallbackHandler handler = new WikiCallbackHandler(
userMgr.getUserDatabase(),
username,
password );
- return doLogin( session, handler, LOGIN_CUSTOM );
+
+ // Execute the user's specified login module
+ Set<Principal> principals = doJAASLogin( UserDatabaseLoginModule.class, handler, m_loginModuleOptions );
+ if (principals.size() > 0)
+ {
+ fireEvent(WikiSecurityEvent.LOGIN_AUTHENTICATED, getLoginPrincipal( principals ), session );
+ for ( Principal principal : principals )
+ {
+ fireEvent( WikiSecurityEvent.PRINCIPAL_ADD, principal, session );
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This method builds a database of login names that are being attempted, and will try to
+ * delay if there are too many requests coming in for the same username.
+ * <p>
+ * The current algorithm uses 2^loginattempts as the delay in milliseconds, i.e.
+ * at 10 login attempts it'll add 1.024 seconds to the login.
+ *
+ * @param username The username that is being logged in
+ */
+ private void delayLogin( String username )
+ {
+ try
+ {
+ m_lastLoginAttempts.cleanup( LASTLOGINS_CLEANUP_TIME );
+ int count = m_lastLoginAttempts.count( username );
+
+ long delay = Math.min( 1<<count, MAX_LOGIN_DELAY );
+ log.debug( "Sleeping for "+delay+" ms to allow login." );
+ Thread.sleep( delay );
+
+ m_lastLoginAttempts.add( username );
+ }
+ catch( InterruptedException e )
+ {
+ // FALLTHROUGH is fine
+ }
}
/**
@@ -307,92 +442,26 @@
/**
* Determines whether this WikiEngine allows users to assert identities using
* cookies instead of passwords. This is determined by inspecting
- * the LoginConfiguration for application <code>JSPWiki-container</code>.
+ * the WikiEngine property {@link #PROP_ALLOW_COOKIE_ASSERTIONS}.
* @return <code>true</code> if cookies are allowed
*/
- public static final boolean allowsCookieAssertions()
+ public final boolean allowsCookieAssertions()
{
- if( !c_useJAAS ) return true;
-
- // Lazily initialize
- if( c_allowsAssertions == null )
- {
- c_allowsAssertions = Boolean.FALSE;
-
- // Figure out whether cookie assertions are allowed
- Configuration loginConfig = (Configuration)AccessController.doPrivileged(new PrivilegedAction()
- {
- public Object run()
- {
- return Configuration.getConfiguration();
- }
- });
-
- if (loginConfig != null)
- {
- AppConfigurationEntry[] configs = loginConfig.getAppConfigurationEntry( LOGIN_CONTAINER );
- if( configs != null )
- {
- for ( int i = 0; i < configs.length; i++ )
- {
- AppConfigurationEntry config = configs[i];
- if ( COOKIE_MODULE.equals( config.getLoginModuleName() ) )
- {
- c_allowsAssertions = Boolean.TRUE;
- }
- }
- }
- }
- }
-
- return c_allowsAssertions.booleanValue();
+ return m_allowsCookieAssertions;
}
/**
* Determines whether this WikiEngine allows users to authenticate using
* cookies instead of passwords. This is determined by inspecting
- * the LoginConfiguration for application <code>JSPWiki-container</code>.
+ * the WikiEngine property {@link #PROP_ALLOW_COOKIE_AUTH}.
* @return <code>true</code> if cookies are allowed for authentication
* @since 2.5.62
*/
- public static final boolean allowsCookieAuthentication()
+ public final boolean allowsCookieAuthentication()
{
- if( !c_useJAAS ) return true;
-
- // Lazily initialize
- if( c_allowsAuthentication == null )
- {
- c_allowsAuthentication = Boolean.FALSE;
-
- // Figure out whether cookie assertions are allowed
- Configuration loginConfig = (Configuration)AccessController.doPrivileged(new PrivilegedAction()
- {
- public Object run()
- {
- return Configuration.getConfiguration();
- }
- });
-
- if (loginConfig != null)
- {
- AppConfigurationEntry[] configs = loginConfig.getAppConfigurationEntry( LOGIN_CONTAINER );
-
- if( configs != null )
- {
- for ( int i = 0; i < configs.length; i++ )
- {
- AppConfigurationEntry config = configs[i];
- if ( COOKIE_AUTHENTICATION_MODULE.equals( config.getLoginModuleName() ) )
- {
- c_allowsAuthentication = Boolean.TRUE;
- }
- }
- }
- }
- }
-
- return c_allowsAuthentication.booleanValue();
+ return m_allowsCookieAuthentication;
}
+
/**
* Determines whether the supplied Principal is a "role principal".
* @param principal the principal to test
@@ -420,99 +489,66 @@
}
/**
- * Log in to the application using a given JAAS LoginConfiguration. Any
- * configuration error
- * @param wikiSession the current wiki session, to which the Subject will be associated
- * @param handler handles callbacks sent by the LoginModules in the configuration
- * @param application the name of the application whose LoginConfiguration should be used
- * @return the result of the login
+ * Instantiates and executes a single JAAS
+ * {@link javax.security.auth.spi.LoginModule}, and returns a Set of
+ * Principals that results from a successful login. The LoginModule is instantiated,
+ * then its {@link javax.security.auth.spi.LoginModule#initialize(Subject, CallbackHandler, Map, Map)}
+ * method is called. The parameters passed to <code>initialize</code> is a
+ * dummy Subject, an empty shared-state Map, and an options Map the caller supplies.
+ *
+ * @param clazz
+ * the LoginModule class to instantiate
+ * @param handler
+ * the callback handler to supply to the LoginModule
+ * @param options
+ * a Map of key/value strings for initializing the LoginModule
+ * @return the set of Principals returned by the JAAS method {@link Subject#getPrincipals()}
* @throws WikiSecurityException
+ * if the LoginModule could not be instantiated for any reason
*/
- private final boolean doLogin( final WikiSession wikiSession, final CallbackHandler handler, final String application ) throws WikiSecurityException
+ protected Set<Principal> doJAASLogin(Class<? extends LoginModule> clazz, CallbackHandler handler, Map<String,String> options) throws WikiSecurityException
{
+ // Instantiate the login module
+ LoginModule loginModule = null;
try
{
- LoginContext loginContext = (LoginContext)AccessController.doPrivileged(new PrivilegedAction()
- {
- public Object run()
- {
- try
- {
- return wikiSession.getLoginContext( application, handler );
- }
- catch( LoginException e )
- {
- log.error( "Couldn't retrieve login configuration.\nMessage="
- + e.getLocalizedMessage() );
- return null;
- }
- }
- });
-
- if( loginContext != null )
- {
- loginContext.login();
- fireEvent( WikiSecurityEvent.LOGIN_INITIATED, null, wikiSession );
- }
- else
- {
- log.error("No login context. Please double-check that JSPWiki found your 'jspwiki.jaas' file or the contents have been appended to your regular JAAS file.");
- return false;
- }
-
- // Fire event for the correct authentication event
- if ( wikiSession.isAnonymous() )
- {
- fireEvent( WikiSecurityEvent.LOGIN_ANONYMOUS, wikiSession.getLoginPrincipal(), wikiSession );
- }
- else if ( wikiSession.isAsserted() )
- {
- fireEvent( WikiSecurityEvent.LOGIN_ASSERTED, wikiSession.getLoginPrincipal(), wikiSession );
- }
- else if ( wikiSession.isAuthenticated() )
- {
- fireEvent( WikiSecurityEvent.LOGIN_AUTHENTICATED, wikiSession.getLoginPrincipal(), wikiSession );
- }
-
- return true;
+ loginModule = clazz.newInstance();
}
- catch( FailedLoginException e )
+ catch (InstantiationException e)
{
- //
- // Just a mistyped password or a cracking attempt. No need to worry
- // and alert the admin
- //
- log.info("Failed login: "+e.getLocalizedMessage());
- fireEvent( WikiSecurityEvent.LOGIN_FAILED, wikiSession.getLoginPrincipal(), wikiSession );
- return false;
+ throw new WikiSecurityException(e.getMessage());
}
- catch( AccountExpiredException e )
+ catch (IllegalAccessException e)
{
- log.info("Expired account: "+e.getLocalizedMessage());
- fireEvent( WikiSecurityEvent.LOGIN_ACCOUNT_EXPIRED, wikiSession.getLoginPrincipal(), wikiSession );
- return false;
+ throw new WikiSecurityException(e.getMessage());
}
- catch( CredentialExpiredException e )
+
+ // Initialize the LoginModule
+ Subject subject = new Subject();
+ loginModule.initialize( subject, handler, EMPTY_MAP, options );
+
+ // Try to log in:
+ boolean loginSucceeded = false;
+ boolean commitSucceeded = false;
+ try
{
- log.info("Credentials expired: "+e.getLocalizedMessage());
- fireEvent( WikiSecurityEvent.LOGIN_CREDENTIAL_EXPIRED, wikiSession.getLoginPrincipal(), wikiSession );
- return false;
+ loginSucceeded = loginModule.login();
+ if (loginSucceeded)
+ {
+ commitSucceeded = loginModule.commit();
+ }
}
- catch( LoginException e )
+ catch (LoginException e)
{
- //
- // This should only be caught if something unforeseen happens,
- // so therefore we can log it as an error.
- //
- log.error( "Couldn't log in.\nMessage="
- + e.getLocalizedMessage() );
- return false;
+ // Login or commit failed! No principal for you!
}
- catch( SecurityException e )
+
+ // If we successfully logged in & committed, return all the principals
+ if (loginSucceeded && commitSucceeded)
{
- log.error( "Could not log in. Please check that your jaas.config file is found.", e );
- return false;
+ return subject.getPrincipals();
}
+ return NO_PRINCIPALS;
}
/**
@@ -571,6 +607,23 @@
return path;
}
+ /**
+ * Returns the first Principal in a set that isn't a {@link com.ecyrd.jspwiki.auth.authorize.Role} or
+ * {@link com.ecyrd.jspwiki.auth.GroupPrincipal}.
+ * @param principals the principal set
+ * @return the login principal
+ */
+ protected Principal getLoginPrincipal(Set<Principal> principals)
+ {
+ for (Principal principal: principals )
+ {
+ if ( isUserPrincipal( principal ) )
+ {
+ return principal;
+ }
+ }
+ return null;
+ }
// events processing .......................................................
@@ -610,5 +663,38 @@
WikiEventManager.fireEvent(this,new WikiSecurityEvent(this,type,principal,target));
}
}
+
+ /**
+ * Initializes the options Map supplied to the configured LoginModule every time it is invoked by
+ * {@link #doLoginModule(Class, CallbackHandler)}. The properties and values extracted from
+ * <code>jspwiki.properties</code> are of the form
+ * <code>jspwiki.loginModule.options.<var>param</var> = <var>value</var>, where
+ * <var>param</var> is the key name, and <var>value</var> is the value.
+ * @param props the properties used to initialize JSPWiki
+ * @throws IllegalArgumentException if any of the keys are duplicated
+ */
+ private void initLoginModuleOptions(Properties props)
+ {
+ for ( Object key : props.keySet() )
+ {
+ String propName = key.toString();
+ if ( propName.startsWith( PREFIX_LOGIN_MODULE_OPTIONS ) )
+ {
+ // Extract the option name and value
+ String optionKey = propName.substring( PREFIX_LOGIN_MODULE_OPTIONS.length() ).trim();
+ if ( optionKey.length() > 0 )
+ {
+ String optionValue = props.getProperty( propName );
+
+ // Make sure the key is unique before stashing the key/value pair
+ if ( m_loginModuleOptions.containsKey( optionKey ) )
+ {
+ throw new IllegalArgumentException( "JAAS LoginModule key " + propName + " cannot be specified twice!" );
+ }
+ m_loginModuleOptions.put( optionKey, optionValue );
+ }
+ }
+ }
+ }
}
Modified: incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/AuthorizationManager.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/AuthorizationManager.java?rev=682143&r1=682142&r2=682143&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/AuthorizationManager.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/AuthorizationManager.java Sun Aug 3 05:15:57 2008
@@ -1,21 +1,22 @@
-/*
- JSPWiki - a JSP-based WikiWiki clone.
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
- Copyright (C) 2001-2003 Janne Jalkanen (Janne.Jalkanen@iki.fi)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
*/
package com.ecyrd.jspwiki.auth;
@@ -102,7 +103,7 @@
private Authorizer m_authorizer = null;
/** Cache for storing ProtectionDomains used to evaluate the local policy. */
- private Map m_cachedPds = new WeakHashMap();
+ private Map<Principal, ProtectionDomain> m_cachedPds = new WeakHashMap<Principal, ProtectionDomain>();
private WikiEngine m_engine = null;
@@ -231,13 +232,11 @@
log.debug( "Checking ACL entries..." );
log.debug( "Acl for this page is: " + acl );
- log.debug( "Checking for principal: " + String.valueOf(aclPrincipals) );
+ log.debug( "Checking for principal: " + String.valueOf( aclPrincipals ) );
log.debug( "Permission: " + permission );
- for( int i = 0; i < aclPrincipals.length; i++ )
+ for( Principal aclPrincipal : aclPrincipals )
{
- Principal aclPrincipal = aclPrincipals[i];
-
// If the ACL principal we're looking at is unresolved,
// try to resolve it here & correct the Acl
if ( aclPrincipal instanceof UnresolvedPrincipal )
@@ -367,9 +366,8 @@
{
String principalName = principal.getName();
Principal[] userPrincipals = session.getPrincipals();
- for( int i = 0; i < userPrincipals.length; i++ )
+ for( Principal userPrincipal : userPrincipals )
{
- Principal userPrincipal = userPrincipals[i];
if( userPrincipal.getName().equals( principalName ) )
{
return true;
@@ -387,6 +385,7 @@
* @param properties the set of properties used to initialize the wiki engine
* @throws WikiException if the AuthorizationManager cannot be initialized
*/
+ @SuppressWarnings("deprecation")
public final void initialize( WikiEngine engine, Properties properties ) throws WikiException
{
m_engine = engine;
@@ -411,17 +410,17 @@
File policyFile = new File( policyURL.getPath() );
m_localPolicy = new LocalPolicy( policyFile, engine.getContentEncoding() );
m_localPolicy.refresh();
- log.info("Initialized default security policy: " + policyFile.getAbsolutePath());
+ log.info( "Initialized default security policy: " + policyFile.getAbsolutePath() );
}
else
{
- StringBuffer sb = new StringBuffer("JSPWiki was unable to initialize the ");
- sb.append("default security policy (WEB-INF/jspwiki.policy) file. ");
- sb.append("Please ensure that the jspwiki.policy file exists in the default location. ");
- sb.append("This file should exist regardless of the existance of a global policy file. ");
- sb.append("The global policy file is identified by the java.security.policy variable. ");
- WikiSecurityException wse = new WikiSecurityException(sb.toString());
- log.fatal(sb.toString(), wse);
+ StringBuffer sb = new StringBuffer( "JSPWiki was unable to initialize the " );
+ sb.append( "default security policy (WEB-INF/jspwiki.policy) file. " );
+ sb.append( "Please ensure that the jspwiki.policy file exists in the default location. " );
+ sb.append( "This file should exist regardless of the existance of a global policy file. " );
+ sb.append( "The global policy file is identified by the java.security.policy variable. " );
+ WikiSecurityException wse = new WikiSecurityException( sb.toString() );
+ log.fatal( sb.toString(), wse );
throw wse;
}
}
@@ -463,7 +462,7 @@
{
try
{
- Class authClass = ClassUtil.findClass( "com.ecyrd.jspwiki.auth.authorize", clazz );
+ Class<?> authClass = ClassUtil.findClass( "com.ecyrd.jspwiki.auth.authorize", clazz );
Object impl = authClass.newInstance();
return impl;
}
@@ -498,16 +497,16 @@
*/
protected boolean allowedByLocalPolicy( Principal[] principals, Permission permission )
{
- for ( int i = 0; i < principals.length; i++ )
+ for ( Principal principal : principals )
{
// Get ProtectionDomain for this Principal from cache, or create new one
- ProtectionDomain pd = (ProtectionDomain)m_cachedPds.get( principals[i] );
+ ProtectionDomain pd = m_cachedPds.get( principal );
if ( pd == null )
{
ClassLoader cl = this.getClass().getClassLoader();
CodeSource cs = new CodeSource( null, (Certificate[])null );
- pd = new ProtectionDomain( cs, null, cl, new Principal[]{ principals[i] } );
- m_cachedPds.put( principals[i], pd );
+ pd = new ProtectionDomain( cs, null, cl, new Principal[]{ principal } );
+ m_cachedPds.put( principal, pd );
}
// Consult the local policy and get the answer
@@ -520,16 +519,16 @@
}
/**
- * Determines whether a Subject posesses a given "static" Permission as
+ * Determines whether a Subject possesses a given "static" Permission as
* defined in the security policy file. This method uses standard Java 2
* security calls to do its work. Note that the current access control
* context's <code>codeBase</code> is effectively <em>this class</em>,
* not that of the caller. Therefore, this method will work best when what
* matters in the policy is <em>who</em> makes the permission check, not
* what the caller's code source is. Internally, this method works by
- * excuting <code>Subject.doAsPrivileged</code> with a privileged action
+ * executing <code>Subject.doAsPrivileged</code> with a privileged action
* that simply calls {@link java.security.AccessController#checkPermission(Permission)}.
- * @link AccessController#checkPermission(java.security.Permission). A
+ * @see AccessController#checkPermission(java.security.Permission) . A
* caught exception (or lack thereof) determines whether the privilege
* is absent (or present).
* @param session the WikiSession whose permission status is being queried
@@ -541,9 +540,9 @@
{
if( !m_useJAAS ) return true;
- Boolean allowed = (Boolean)WikiSession.doPrivileged( session, new PrivilegedAction()
+ Boolean allowed = (Boolean) WikiSession.doPrivileged( session, new PrivilegedAction<Boolean>()
{
- public Object run()
+ public Boolean run()
{
try
{
Modified: incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/Authorizer.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/Authorizer.java?rev=682143&r1=682142&r2=682143&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/Authorizer.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/Authorizer.java Sun Aug 3 05:15:57 2008
@@ -1,21 +1,22 @@
-/*
+/*
JSPWiki - a JSP-based WikiWiki clone.
- Copyright (C) 2001-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
*/
package com.ecyrd.jspwiki.auth;
Modified: incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/GroupPrincipal.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/GroupPrincipal.java?rev=682143&r1=682142&r2=682143&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/GroupPrincipal.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/GroupPrincipal.java Sun Aug 3 05:15:57 2008
@@ -1,15 +1,22 @@
-/*
- * JSPWiki - a JSP-based WikiWiki clone. Copyright (C) 2001-2003 Janne Jalkanen
- * (Janne.Jalkanen@iki.fi) This program is free software; you can redistribute
- * it and/or modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1 of the
- * License, or (at your option) any later version. This program is distributed
- * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
- * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU Lesser General Public License for more details. You should have
- * received a copy of the GNU Lesser General Public License along with this
- * program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place, Suite 330, Boston, MA 02111-1307 USA
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
*/
package com.ecyrd.jspwiki.auth;
Modified: incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/NoSuchPrincipalException.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/NoSuchPrincipalException.java?rev=682143&r1=682142&r2=682143&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/NoSuchPrincipalException.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/NoSuchPrincipalException.java Sun Aug 3 05:15:57 2008
@@ -1,21 +1,22 @@
/*
JSPWiki - a JSP-based WikiWiki clone.
- Copyright (C) 2001-2003 Janne Jalkanen (Janne.Jalkanen@iki.fi)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
*/
package com.ecyrd.jspwiki.auth;
Modified: incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PrincipalComparator.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PrincipalComparator.java?rev=682143&r1=682142&r2=682143&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PrincipalComparator.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/PrincipalComparator.java Sun Aug 3 05:15:57 2008
@@ -1,21 +1,22 @@
-/*
- JSPWiki - a JSP-based WikiWiki clone.
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
- Copyright (C) 2001-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
*/
package com.ecyrd.jspwiki.auth;
@@ -30,7 +31,7 @@
* @since 2.3
*/
public class PrincipalComparator
- implements Comparator<Principal>, Serializable
+ implements Comparator<Principal>, Serializable
{
private static final long serialVersionUID = 1L;
|