Merged V3.2 to HEAD

19617: ALF-1890: Improvements to make ALL WebDAV methods retryable
      - Solution from PutMethod promoted to request wrapper that will handle ALL calls to getInputStream and getReader
   19623: ALF-1890: Correction to previous checkin to allow defaulting of request body charset
   19624: ALF-2231: Merged DEV/BELARUS/V2.2-2009_12_01 to V3.2
      17704: ENH-681: alfresco webdav does not respect webdav locks
   19647: ALF-2231: Merged DEV/BELARUS/V2.2-2009_12_01 to V3.2 
      17704: ENH-681: alfresco webdav does not respect webdav locks 
   19655: ALF-1997: Share with NTLM SSO enabled now supports form-based login as a fallback
      - If you use the new sample webscript framework NTLM config and go to http://localhost:8080/share/page?f=default&pt=login you can log in as any user in the repository, even an LDAP user!
      - When you log out again, it will fall back to your NTLM credentials
      - WebScriptNTLMAuthenticationFilter modified to create the session user for the /api/login call
      - AlfrescoAuthenticator modified to cope with cookie propagation as well as ticket propagation
      - LoginServlet bugfixed to kill the old session on login rather than the new one!


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@19690 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Dave Ward
2010-03-31 11:51:35 +00:00
parent 5135042796
commit 681676236f
3 changed files with 232 additions and 50 deletions

View File

@@ -19,12 +19,7 @@
package org.alfresco.repo.webdav; package org.alfresco.repo.webdav;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.HashMap; import java.util.HashMap;
@@ -34,14 +29,13 @@ import javax.servlet.http.HttpServletResponse;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.encoding.ContentCharsetFinder; import org.alfresco.repo.content.encoding.ContentCharsetFinder;
import org.alfresco.service.cmr.lock.LockStatus;
import org.alfresco.service.cmr.model.FileExistsException; import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException; import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.util.TempFileProvider;
import org.springframework.util.FileCopyUtils;
/** /**
* Implements the WebDAV PUT method * Implements the WebDAV PUT method
@@ -55,8 +49,6 @@ public class PutMethod extends WebDAVMethod
private String m_strContentType = null; private String m_strContentType = null;
private boolean m_expectHeaderPresent = false; private boolean m_expectHeaderPresent = false;
private File requestBody = null;
/** /**
* Default constructor * Default constructor
*/ */
@@ -91,16 +83,8 @@ public class PutMethod extends WebDAVMethod
*/ */
protected void parseRequestBody() throws WebDAVServerException protected void parseRequestBody() throws WebDAVServerException
{ {
requestBody = TempFileProvider.createTempFile("webdav_PUT_", ".bin"); // Nothing to do in this method, the body contains
try // the content it will be dealt with later
{
OutputStream out = new FileOutputStream(requestBody);
FileCopyUtils.copy(m_request.getInputStream(), out);
}
catch (IOException e)
{
throw new WebDAVServerException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
}
} }
/** /**
@@ -156,6 +140,16 @@ public class PutMethod extends WebDAVMethod
} }
} }
LockStatus lockSts = getLockService().getLockStatus(contentNodeInfo.getNodeRef());
String userName = getDAVHelper().getAuthenticationService().getCurrentUserName();
String owner = (String) getNodeService().getProperty(contentNodeInfo.getNodeRef(), ContentModel.PROP_LOCK_OWNER);
if (lockSts == LockStatus.LOCKED || (lockSts == LockStatus.LOCK_OWNER && !userName.equals(owner)))
{
// Indicate that the resource is locked
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
}
// Access the content // Access the content
ContentWriter writer = fileFolderService.getWriter(contentNodeInfo.getNodeRef()); ContentWriter writer = fileFolderService.getWriter(contentNodeInfo.getNodeRef());
// set content properties // set content properties
@@ -166,13 +160,13 @@ public class PutMethod extends WebDAVMethod
} }
else else
{ {
String guessedMimetype = getMimetypeService().guessMimetype(getPath()); String guessedMimetype = getMimetypeService().guessMimetype(contentNodeInfo.getName());
mimetype = guessedMimetype; mimetype = guessedMimetype;
} }
writer.setMimetype(mimetype); writer.setMimetype(mimetype);
// Get the input stream from the request data // Get the input stream from the request data
InputStream is = new FileInputStream(requestBody); InputStream is = m_request.getInputStream();
is = is.markSupported() ? is : new BufferedInputStream(is); is = is.markSupported() ? is : new BufferedInputStream(is);
ContentCharsetFinder charsetFinder = getMimetypeService().getContentCharsetFinder(); ContentCharsetFinder charsetFinder = getMimetypeService().getContentCharsetFinder();
@@ -185,17 +179,4 @@ public class PutMethod extends WebDAVMethod
// Set the response status, depending if the node existed or not // Set the response status, depending if the node existed or not
m_response.setStatus(created ? HttpServletResponse.SC_CREATED : HttpServletResponse.SC_NO_CONTENT); m_response.setStatus(created ? HttpServletResponse.SC_CREATED : HttpServletResponse.SC_NO_CONTENT);
} }
@Override
protected void cleanup()
{
try
{
requestBody.delete();
}
catch (Throwable t)
{
logger.error("Failed to delete temp file", t);
}
}
} }

View File

@@ -18,14 +18,22 @@
*/ */
package org.alfresco.repo.webdav; package org.alfresco.repo.webdav;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
@@ -50,11 +58,13 @@ import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.dom4j.DocumentHelper; import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat; import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter; import org.dom4j.io.XMLWriter;
import org.springframework.util.FileCopyUtils;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
@@ -80,6 +90,9 @@ public abstract class WebDAVMethod
protected HttpServletRequest m_request; protected HttpServletRequest m_request;
protected HttpServletResponse m_response; protected HttpServletResponse m_response;
private File m_requestBody;
private ServletInputStream m_inputStream;
private BufferedReader m_reader;
// WebDAV helper // WebDAV helper
@@ -116,19 +129,129 @@ public abstract class WebDAVMethod
/** /**
* Set the request/response details * Set the request/response details
* *
* @param req HttpServletRequest * @param req
* @param resp HttpServletResponse * HttpServletRequest
* @param registry ServiceRegistry * @param resp
* @param rootNode NodeRef * HttpServletResponse
* @param registry
* ServiceRegistry
* @param rootNode
* NodeRef
*/ */
public void setDetails(HttpServletRequest req, HttpServletResponse resp, WebDAVHelper davHelper, NodeRef rootNode) public void setDetails(final HttpServletRequest req, HttpServletResponse resp, WebDAVHelper davHelper,
NodeRef rootNode)
{ {
m_request = req; // Wrap the request so that it is 'retryable'. Calls to getInputStream() and getReader() will result in the
m_response = resp; // request body being read into an intermediate file.
m_davHelper = davHelper; this.m_request = new HttpServletRequestWrapper(req)
m_rootNodeRef = rootNode; {
m_strPath = WebDAV.getRepositoryPath(req); @Override
public ServletInputStream getInputStream() throws IOException
{
if (WebDAVMethod.this.m_reader != null)
{
throw new IllegalStateException("Reader in use");
}
if (WebDAVMethod.this.m_inputStream == null)
{
final FileInputStream in = new FileInputStream(getRequestBodyAsFile(req));
WebDAVMethod.this.m_inputStream = new ServletInputStream()
{
@Override
public int read() throws IOException
{
return in.read();
}
@Override
public int read(byte b[]) throws IOException
{
return in.read(b);
}
@Override
public int read(byte b[], int off, int len) throws IOException
{
return in.read(b, off, len);
}
@Override
public long skip(long n) throws IOException
{
return in.skip(n);
}
@Override
public int available() throws IOException
{
return in.available();
}
@Override
public void close() throws IOException
{
in.close();
}
@Override
public void mark(int readlimit)
{
in.mark(readlimit);
}
@Override
public void reset() throws IOException
{
in.reset();
}
@Override
public boolean markSupported()
{
return in.markSupported();
}
};
}
return WebDAVMethod.this.m_inputStream;
}
@Override
public BufferedReader getReader() throws IOException
{
if (WebDAVMethod.this.m_inputStream != null)
{
throw new IllegalStateException("Input Stream in use");
}
if (WebDAVMethod.this.m_reader == null)
{
String encoding = req.getCharacterEncoding();
WebDAVMethod.this.m_reader = new BufferedReader(new InputStreamReader(new FileInputStream(
getRequestBodyAsFile(req)), encoding == null ? "ISO-8859-1" : encoding));
}
return WebDAVMethod.this.m_reader;
}
};
this.m_response = resp;
this.m_davHelper = davHelper;
this.m_rootNodeRef = rootNode;
this.m_strPath = WebDAV.getRepositoryPath(req);
}
private File getRequestBodyAsFile(HttpServletRequest req) throws IOException
{
if (this.m_requestBody == null)
{
this.m_requestBody = TempFileProvider.createTempFile("webdav_" + req.getMethod() + "_", ".bin");
OutputStream out = new FileOutputStream(this.m_requestBody);
FileCopyUtils.copy(req.getInputStream(), out);
}
return this.m_requestBody;
} }
/** /**
@@ -168,6 +291,10 @@ public abstract class WebDAVMethod
{ {
public Object execute() throws Exception public Object execute() throws Exception
{ {
// Reset the request input stream / reader state
WebDAVMethod.this.m_inputStream = null;
WebDAVMethod.this.m_reader = null;
executeImpl(); executeImpl();
return null; return null;
} }
@@ -201,7 +328,19 @@ public abstract class WebDAVMethod
} }
finally finally
{ {
cleanup(); // Remove temporary file if created
if (this.m_requestBody != null)
{
try
{
this.m_requestBody.delete();
this.m_requestBody = null;
}
catch (Throwable t)
{
WebDAVMethod.logger.error("Failed to delete temp file", t);
}
}
} }
} }
@@ -650,10 +789,6 @@ public abstract class WebDAVMethod
return ns.toString(); return ns.toString();
} }
protected void cleanup()
{
}
/** /**
* Checks if write operation can be performed on node. * Checks if write operation can be performed on node.
* *

View File

@@ -19,6 +19,7 @@
package org.alfresco.repo.webdav.auth; package org.alfresco.repo.webdav.auth;
import java.io.IOException; import java.io.IOException;
import java.io.Reader;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
@@ -37,6 +38,8 @@ import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.json.JSONException;
import org.json.JSONObject;
/** /**
* A base class for authentication filters. Handles management of the session user. * A base class for authentication filters. Handles management of the session user.
@@ -283,4 +286,67 @@ public abstract class BaseAuthenticationFilter
*/ */
protected abstract Log getLogger(); protected abstract Log getLogger();
/**
* Handles the login form directly, allowing management of the session user.
*
* @param req
* the request
* @param res
* the response
* @throws IOException
* Signals that an I/O exception has occurred.
* @throws ServletException
* on error
*/
protected void handleLoginForm(HttpServletRequest req, HttpServletResponse res) throws IOException,
ServletException
{
// Invalidate current session
HttpSession session = req.getSession(false);
if (session != null)
{
session.invalidate();
}
StringBuilder out = new StringBuilder(1024);
Reader in = req.getReader();
char[] buff = new char[1024];
int charsRead;
while ((charsRead = in.read(buff)) != -1)
{
out.append(buff, 0, charsRead);
}
in.close();
try
{
JSONObject json = new JSONObject(out.toString());
String username = json.getString("username");
String password = json.getString("password");
if (username == null || username.length() == 0)
{
res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Username not specified");
return;
}
if (password == null)
{
res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Password not specified");
return;
}
authenticationService.authenticate(username, password.toCharArray());
session = req.getSession();
createUserEnvironment(session, username, authenticationService.getCurrentTicket(session.getId()), false);
res.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
catch (AuthenticationException e)
{
res.sendError(HttpServletResponse.SC_FORBIDDEN, "Login failed");
}
catch (JSONException jErr)
{
res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unable to parse JSON POST body: " + jErr.getMessage());
}
}
} }