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;
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.OutputStream;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.HashMap;
@@ -34,14 +29,13 @@ import javax.servlet.http.HttpServletResponse;
import org.alfresco.model.ContentModel;
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.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.TempFileProvider;
import org.springframework.util.FileCopyUtils;
/**
* Implements the WebDAV PUT method
@@ -55,8 +49,6 @@ public class PutMethod extends WebDAVMethod
private String m_strContentType = null;
private boolean m_expectHeaderPresent = false;
private File requestBody = null;
/**
* Default constructor
*/
@@ -91,16 +83,8 @@ public class PutMethod extends WebDAVMethod
*/
protected void parseRequestBody() throws WebDAVServerException
{
requestBody = TempFileProvider.createTempFile("webdav_PUT_", ".bin");
try
{
OutputStream out = new FileOutputStream(requestBody);
FileCopyUtils.copy(m_request.getInputStream(), out);
}
catch (IOException e)
{
throw new WebDAVServerException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
}
// Nothing to do in this method, the body contains
// the content it will be dealt with later
}
/**
@@ -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
ContentWriter writer = fileFolderService.getWriter(contentNodeInfo.getNodeRef());
// set content properties
@@ -166,13 +160,13 @@ public class PutMethod extends WebDAVMethod
}
else
{
String guessedMimetype = getMimetypeService().guessMimetype(getPath());
String guessedMimetype = getMimetypeService().guessMimetype(contentNodeInfo.getName());
mimetype = guessedMimetype;
}
writer.setMimetype(mimetype);
// 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);
ContentCharsetFinder charsetFinder = getMimetypeService().getContentCharsetFinder();
@@ -185,17 +179,4 @@ public class PutMethod extends WebDAVMethod
// Set the response status, depending if the node existed or not
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;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
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.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.springframework.util.FileCopyUtils;
import org.w3c.dom.Document;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
@@ -80,6 +90,9 @@ public abstract class WebDAVMethod
protected HttpServletRequest m_request;
protected HttpServletResponse m_response;
private File m_requestBody;
private ServletInputStream m_inputStream;
private BufferedReader m_reader;
// WebDAV helper
@@ -116,21 +129,131 @@ public abstract class WebDAVMethod
/**
* Set the request/response details
*
* @param req HttpServletRequest
* @param resp HttpServletResponse
* @param registry ServiceRegistry
* @param rootNode NodeRef
* @param req
* HttpServletRequest
* @param resp
* 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;
m_response = resp;
m_davHelper = davHelper;
m_rootNodeRef = rootNode;
// Wrap the request so that it is 'retryable'. Calls to getInputStream() and getReader() will result in the
// request body being read into an intermediate file.
this.m_request = new HttpServletRequestWrapper(req)
{
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;
}
/**
* Override and return <tt>true</tt> if the method is a query method only. The default implementation
* returns <tt>false</tt>.
@@ -168,6 +291,10 @@ public abstract class WebDAVMethod
{
public Object execute() throws Exception
{
// Reset the request input stream / reader state
WebDAVMethod.this.m_inputStream = null;
WebDAVMethod.this.m_reader = null;
executeImpl();
return null;
}
@@ -201,7 +328,19 @@ public abstract class WebDAVMethod
}
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();
}
protected void cleanup()
{
}
/**
* Checks if write operation can be performed on node.
*

View File

@@ -19,6 +19,7 @@
package org.alfresco.repo.webdav.auth;
import java.io.IOException;
import java.io.Reader;
import javax.servlet.ServletContext;
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.transaction.TransactionService;
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.
@@ -282,5 +285,68 @@ public abstract class BaseAuthenticationFilter
* @return Log
*/
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());
}
}
}