📄 davtransaction.java
字号:
/* ========================================================================== *
* Copyright (C) 2004-2005 Pier Fumagalli <http://www.betaversion.org/~pier/> *
* All rights reserved. *
* ========================================================================== *
* *
* Licensed 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.sslexplorer.vfs.webdav;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.maverick.crypto.encoders.Base64;
import com.sslexplorer.boot.Util;
import com.sslexplorer.core.CoreServlet;
import com.sslexplorer.core.CoreUtil;
import com.sslexplorer.core.ServletRequestAdapter;
import com.sslexplorer.core.ServletResponseAdapter;
import com.sslexplorer.security.AccountLockedException;
import com.sslexplorer.security.AuthenticationModuleManager;
import com.sslexplorer.security.AuthenticationScheme;
import com.sslexplorer.security.AuthenticationSchemeSequence;
import com.sslexplorer.security.Constants;
import com.sslexplorer.security.Credentials;
import com.sslexplorer.security.InvalidLoginCredentialsException;
import com.sslexplorer.security.LogonController;
import com.sslexplorer.security.SessionInfo;
import com.sslexplorer.security.WebDAVAuthenticationModule;
import com.sslexplorer.security.actions.LogonAction;
import com.sslexplorer.vfs.utils.DAVCredentialsCashe;
/**
* <p>A simple wrapper isolating the Java Servlet API from this
* <a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
* implementation.</p>
*
* @author <a href="http://www.betaversion.org/~pier/">Pier Fumagalli</a>
*/
public class DAVTransaction {
private static Log log = LogFactory.getLog(DAVTransaction.class);
public final static String ATTR_EXPECTING_REALM_AUTHENTICATION = "expectingRealmAuth";
public final static String ATTR_DEREGISTER_SUB_AUTHS = "deregisterSubAuths";
public final static String ATTR_AUTH_ATTEMPTS = "authAttempts";
/**
* <p>The identifyication of the <code>infinity</code> value
* in the <code>Depth</code> header.</p>
*/
public static final int INFINITY = Integer.MAX_VALUE;
/** <p>The nested {@link HttpServletRequest}.</p> */
private HttpServletRequest req = null;
/** <p>The nested {@link HttpServletResponse}.</p> */
private HttpServletResponse res = null;
/** <p>The {@link URI} associated with the base of the repository.</p> */
private URI base = null;
/** <p>The {@link URI} session info for this transaction. contains user etc.</p> */
private SessionInfo sessionInfo;
/** <p>The path for this transaction. contains user etc.</p> */
private String path;
/** <p>A cache of resources for the life of this transaction */
private Map resourceCache;
/** <p>The current credentials object being used for authentication to the resources */
private DAVCredentials currentCredentials;
/* ====================================================================== */
/* Constructors */
/* ====================================================================== */
/**
* <p>Create a new {@link DAVTransaction} instance.</p>
*/
public DAVTransaction(ServletRequest request, ServletResponse response)
throws ServletException, DAVAuthenticationRequiredException {
if (request == null) throw new NullPointerException("Null request");
if (response == null) throw new NullPointerException("Null response");
this.req = (HttpServletRequest) request;
this.res = (HttpServletResponse) response;
this.resourceCache = new HashMap();
try {
this.sessionInfo = CoreServlet.getServlet().getLogonController().getSessionInfo((HttpServletRequest) request);
if(this.sessionInfo == null) {
String ticket = request.getParameter("ticket");
if(ticket != null) {
Map activeSessions = CoreServlet.getServlet().getLogonController().getActiveSessions();
for(Iterator i = activeSessions.values().iterator(); this.sessionInfo == null && i.hasNext(); ) {
SessionInfo session = (SessionInfo)i.next();
if(ticket.equals(session.getHttpSession().getAttribute(Constants.WEB_FOLDER_LAUNCH_TICKET))) {
this.sessionInfo = session;
}
}
if(this.sessionInfo != null) {
CoreServlet.getServlet().getLogonController().addCookies(new ServletRequestAdapter((HttpServletRequest)request),
new ServletResponseAdapter((HttpServletResponse)response), sessionInfo.getLogonTicket(), sessionInfo.getUser());
}
}
}
String scheme = this.req.getScheme();
String host = this.req.getServerName();
String basePath = CoreUtil.doStandardReplacements(sessionInfo, DAVUtilities.concatenatePaths(this.req.getServletPath(), this.req.getPathInfo()));
int port = this.req.getServerPort();
this.base = new URI(scheme, null, host, port, basePath, null, null);
this.base = this.base.normalize();
path = DAVUtilities.stripTrailingSlash(DAVUtilities.stripLeadingSlash(DAVUtilities.stripFirstPath(base.getPath())));
} catch (URISyntaxException exception) {
throw new ServletException("Unable to create base URI", exception);
}
}
public void putCachedResource(DAVResource resource) {
String key = DAVUtilities.concatenatePaths(resource.getMount().getMountString(), resource.getRelativePath());
resourceCache.put(key, resource);
}
public DAVResource getCachedResource(String fullPath) {
return (DAVResource)resourceCache.get(fullPath);
}
public boolean verifyAuthorization() throws IOException, DAVAuthenticationRequiredException {
/**
* SSL-Explorer authentication should be performed here.
*/
String expectingRealm = (String) req.getSession().getAttribute(ATTR_EXPECTING_REALM_AUTHENTICATION);
try {
if (!CoreServlet.getServlet().getSystemDatabase().verifyIPAddress(req.getRemoteAddr())) {
if(log.isDebugEnabled())
log.debug(req.getRemoteHost() + " is not authorized");
res.setStatus(HttpServletResponse.SC_FORBIDDEN);
return false;
}
else {
/**
* We may already have the session info
*/
if(sessionInfo!=null)
return true;
/**
* No so lets get it from the request
*/
if (CoreServlet.getServlet().getLogonController().hasClientLoggedOn(req, res) == LogonController.LOGGED_ON) {
sessionInfo = CoreServlet.getServlet().getLogonController().getSessionInfo(req);
return true;
}
}
if(log.isDebugEnabled())
log.debug("Requesting HTTP authentication for SSL-Explorer logon.");
expectingRealm = WebDAVAuthenticationModule.DEFAULT_REALM;
req.getSession().setAttribute(Constants.ORIGINAL_REQUEST, Util.getOriginalRequest(req));
if(log.isDebugEnabled())
log.debug("Expecting authentication " + expectingRealm);
// Now test for the authorization
boolean hasAuthorization = req.getHeader("Authorization") != null && Boolean.TRUE.equals(req.getSession().getAttribute(Constants.AUTH_SENT));
if (expectingRealm.equals(WebDAVAuthenticationModule.DEFAULT_REALM) && !hasAuthorization) {
if(log.isDebugEnabled())
log.debug("Creating new HTTP authentication authentication session");
/*
* TODO make this more secure. This makes it possible to logon using a WebDAV url with HTTP basic authentication
* then continue to use other areas of SSL-Explorer - bypassing any stricter authentication schemes
*/
AuthenticationSchemeSequence seq = AuthenticationModuleManager.getInstance().getSchemeForAuthenticationModuleInUse(WebDAVAuthenticationModule.MODULE_NAME);
if(seq == null || !seq.getEnabled()) {
log.error("User cannot authenticate via WebDAV using only HTTP BASIC authentication as the current policy does not allow this.");
res.sendError(DAVStatus.SC_FORBIDDEN, "You cannot authenticate via WebDAV using only HTTP BASIC authentication as the current policy does not allow this.");
return false;
}
seq.addModule(WebDAVAuthenticationModule.MODULE_NAME);
seq.init(req.getSession());
seq.nextAuthenticationModule();
req.getSession().setAttribute(Constants.AUTH_SENT, Boolean.TRUE);
req.getSession().setAttribute(Constants.AUTH_SESSION, seq);
WebDAVAuthenticationModule.sendAuthorizationError(req, res, WebDAVAuthenticationModule.DEFAULT_REALM);
return false;
}
if (!hasAuthorization) {
WebDAVAuthenticationModule.sendAuthorizationError(req, res, expectingRealm);
return false;
}
else {
String authorization = req.getHeader("Authorization");
int idx = authorization.indexOf(' ');
if (idx == -1 || idx == authorization.length() - 1) {
WebDAVAuthenticationModule.sendAuthorizationError(req, res, expectingRealm);
return false;
}
// Authenticate the user
String method = authorization.substring(0, idx);
if (!method.equalsIgnoreCase("basic")) {
WebDAVAuthenticationModule.sendAuthorizationError(req, res, expectingRealm);
return false;
}
// Extract the credentials - should be ticket:tunnel
String encoded = authorization.substring(idx + 1);
String credentials = new String(Base64.decode(encoded));
idx = credentials.indexOf(':');
if (idx == 0 || idx == -1) {
WebDAVAuthenticationModule.sendAuthorizationError(req, res, expectingRealm);
return false;
}
// Get the user credentials
String username = credentials.substring(0, idx);
if (expectingRealm.equals(WebDAVAuthenticationModule.DEFAULT_REALM)) {
AuthenticationSchemeSequence authScheme = (AuthenticationSchemeSequence) req.getSession().getAttribute(Constants.AUTH_SESSION);
if(authScheme == null) {
throw new Exception("No authentication scheme initialised.");
}
LogonAction.authenticate(authScheme, req);
LogonAction.finishAuthentication(authScheme, req, res);
}
else {
if(log.isDebugEnabled())
log.debug("Logging " + username + " [" + req.getRemoteHost() + "] onto realm " + expectingRealm
+ " using Basic authentication for session " + req.getSession().getId());
//subAuths.put(expectingRealm, new AuthPair(username, password.toCharArray()));
req.getSession().removeAttribute(ATTR_EXPECTING_REALM_AUTHENTICATION);
}
req.getSession().removeAttribute(Constants.AUTH_SENT);
/* Logging method */
if(log.isDebugEnabled())
log.debug(req.getMethod() + ' ' + req.getRequestURI() + ' ' + req.getProtocol());
// We now can get the sessionInfo object for this session
sessionInfo = CoreServlet.getServlet().getLogonController().getSessionInfo(req);
return true;
}
} catch (InvalidLoginCredentialsException ex) {
log.error(ex);
WebDAVAuthenticationModule.sendAuthorizationError(req, res, expectingRealm);
return false;
}
catch (AccountLockedException ex) {
log.error(ex);
res.sendError(DAVStatus.SC_FORBIDDEN, ex.getMessage());
return false;
} catch(Exception ex) {
log.error("Unexpected error", ex);
res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return false;
}
}
public HttpServletResponse getResponse() {
return (HttpServletResponse) res;
}
/* ====================================================================== */
/* Request methods */
/* ====================================================================== */
/**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -