1 17 package com.sslexplorer.vfs.webdav; 18 19 import java.io.IOException ; 20 import java.io.InputStream ; 21 import java.io.OutputStream ; 22 import java.io.OutputStreamWriter ; 23 import java.io.PrintWriter ; 24 import java.net.URI ; 25 import java.net.URISyntaxException ; 26 import java.util.Date ; 27 import java.util.HashMap ; 28 import java.util.Map ; 29 30 import javax.servlet.ServletException ; 31 import javax.servlet.ServletRequest ; 32 import javax.servlet.ServletResponse ; 33 import javax.servlet.http.HttpServletRequest ; 34 import javax.servlet.http.HttpServletResponse ; 35 36 import org.apache.commons.logging.Log; 37 import org.apache.commons.logging.LogFactory; 38 39 import com.maverick.crypto.encoders.Base64; 40 import com.sslexplorer.boot.Util; 41 import com.sslexplorer.core.ServletRequestAdapter; 42 import com.sslexplorer.core.ServletResponseAdapter; 43 import com.sslexplorer.core.UserDatabaseManager; 44 import com.sslexplorer.policyframework.LaunchSession; 45 import com.sslexplorer.policyframework.LaunchSessionFactory; 46 import com.sslexplorer.security.AccountLockedException; 47 import com.sslexplorer.security.AuthenticationModuleManager; 48 import com.sslexplorer.security.AuthenticationScheme; 49 import com.sslexplorer.security.Constants; 50 import com.sslexplorer.security.DefaultAuthenticationScheme; 51 import com.sslexplorer.security.InvalidLoginCredentialsException; 52 import com.sslexplorer.security.LogonController; 53 import com.sslexplorer.security.LogonControllerFactory; 54 import com.sslexplorer.security.PasswordCredentials; 55 import com.sslexplorer.security.SessionInfo; 56 import com.sslexplorer.security.SystemDatabaseFactory; 57 import com.sslexplorer.security.User; 58 import com.sslexplorer.security.WebDAVAuthenticationModule; 59 import com.sslexplorer.security.actions.LogonAction; 60 import com.sslexplorer.vfs.VFSResource; 61 62 70 public class DAVTransaction { 71 72 private static Log log = LogFactory.getLog(DAVTransaction.class); 73 74 public final static String ATTR_EXPECTING_REALM_AUTHENTICATION = "expectingRealmAuth"; 75 public final static String ATTR_DEREGISTER_SUB_AUTHS = "deregisterSubAuths"; 76 public final static String ATTR_AUTH_ATTEMPTS = "authAttempts"; 77 78 84 public static final int INFINITY = Integer.MAX_VALUE; 85 86 91 private HttpServletRequest req = null; 92 97 private HttpServletResponse res = null; 98 103 private URI base = null; 104 109 private String path; 110 114 private Map resourceCache; 115 116 119 private SessionInfo sessionInfo; 120 121 126 128 129 130 131 136 public DAVTransaction(ServletRequest request, ServletResponse response) 137 throws ServletException { 139 if (request == null) 140 throw new NullPointerException ("Null request"); 141 if (response == null) 142 throw new NullPointerException ("Null response"); 143 this.req = (HttpServletRequest ) request; 144 this.res = (HttpServletResponse ) response; 145 this.resourceCache = new HashMap (); 146 147 152 153 String launchId = request.getParameter(LaunchSession.LAUNCH_ID); 154 155 if (launchId != null) { 156 LaunchSession launchSession = LaunchSessionFactory.getInstance().getLaunchSession(launchId); 157 if (launchSession != null) { 158 sessionInfo = launchSession.getSession(); 159 LogonControllerFactory.getInstance().addCookies(new ServletRequestAdapter((HttpServletRequest ) request), 160 new ServletResponseAdapter((HttpServletResponse ) response), 161 launchSession.getSession().getLogonTicket(), 162 launchSession.getSession()); 163 sessionInfo.access(); 164 } else if (log.isDebugEnabled()) 165 log.debug("Could not locate session using ticket"); 166 } 167 } 168 169 void configureFromRequest() throws URISyntaxException { 170 String scheme = this.req.getScheme(); 171 String host = this.req.getServerName(); 172 String basePath = DAVUtilities.concatenatePaths(this.req.getServletPath(), 173 this.req.getPathInfo()); 174 int port = this.req.getServerPort(); 175 this.base = new URI (scheme, null, host, port, basePath, null, null); 176 this.base = this.base.normalize(); 177 path = DAVUtilities.stripTrailingSlash(DAVUtilities.stripLeadingSlash(DAVUtilities.stripFirstPath(base.getPath()))); 178 179 } 180 181 public void putCachedResource(VFSResource resource) { 182 String key = DAVUtilities.concatenatePaths(resource.getMount().getMountString(), resource.getRelativePath()); 183 resourceCache.put(key, resource); 184 } 185 186 public VFSResource getCachedResource(String fullPath) { 187 return (VFSResource) resourceCache.get(fullPath); 188 } 189 190 public boolean verifyAuthorization() throws IOException , DAVAuthenticationRequiredException { 191 192 String expectingRealm = (String ) req.getSession().getAttribute(ATTR_EXPECTING_REALM_AUTHENTICATION); 193 194 try { 195 if (!SystemDatabaseFactory.getInstance().verifyIPAddress(req.getRemoteAddr())) { 196 if (log.isDebugEnabled()) 197 log.debug(req.getRemoteHost() + " is not authorized"); 198 res.setStatus(HttpServletResponse.SC_FORBIDDEN); 199 return false; 200 } else { 201 202 208 if (sessionInfo != null) { 209 configureFromRequest(); 210 return true; 211 } 212 213 217 sessionInfo = LogonControllerFactory.getInstance().getSessionInfo(req); 218 if (sessionInfo!=null) { 219 configureFromRequest(); 220 return true; 221 } 222 } 223 224 if (log.isDebugEnabled()) 225 log.debug("Requesting HTTP authentication for SSL-Explorer logon."); 226 227 228 req.getSession().setAttribute(Constants.ORIGINAL_REQUEST, Util.getOriginalRequest(req)); 229 230 if (log.isDebugEnabled()) 231 log.debug("Expecting authentication " + expectingRealm); 232 233 boolean hasAuthorization = req.getHeader("Authorization") != null && Boolean.TRUE.equals(req.getSession() 235 .getAttribute(Constants.AUTH_SENT)); 236 237 if (!hasAuthorization) { 238 if (log.isDebugEnabled()) 239 log.debug("Creating new HTTP authentication authentication session"); 240 241 AuthenticationScheme seq = AuthenticationModuleManager.getInstance() 242 .getSchemeForAuthenticationModuleInUse(WebDAVAuthenticationModule.MODULE_NAME); 243 if (seq == null || !seq.getEnabled()) { 244 log.error("User cannot authenticate via WebDAV using only HTTP BASIC authentication as the current policy does not allow this."); 245 res.sendError(DAVStatus.SC_FORBIDDEN, 246 "You cannot authenticate via WebDAV using only HTTP BASIC authentication as the current policy does not allow this."); 247 return false; 248 } 249 seq.addModule(WebDAVAuthenticationModule.MODULE_NAME); 250 seq.init(req.getSession()); 251 seq.nextAuthenticationModule(); 252 req.getSession().setAttribute(Constants.AUTH_SENT, Boolean.TRUE); 253 req.getSession().setAttribute(Constants.AUTH_SESSION, seq); 254 req.getSession().setAttribute(ATTR_EXPECTING_REALM_AUTHENTICATION, WebDAVAuthenticationModule.DEFAULT_REALM); 255 WebDAVAuthenticationModule.sendAuthorizationError(req, res, WebDAVAuthenticationModule.DEFAULT_REALM); 256 return false; 257 } else { 258 String authorization = req.getHeader("Authorization"); 259 int idx = authorization.indexOf(' '); 260 if (idx == -1 || idx == authorization.length() - 1) { 261 WebDAVAuthenticationModule.sendAuthorizationError(req, res, expectingRealm); 262 return false; 263 } 264 String method = authorization.substring(0, idx); 266 if (!method.equalsIgnoreCase("basic")) { 267 WebDAVAuthenticationModule.sendAuthorizationError(req, res, expectingRealm); 268 return false; 269 } 270 271 String encoded = authorization.substring(idx + 1); 273 String credentials = new String (Base64.decode(encoded)); 274 275 idx = credentials.indexOf(':'); 276 if (idx == 0 || idx == -1) { 277 WebDAVAuthenticationModule.sendAuthorizationError(req, res, expectingRealm); 278 return false; 279 } 280 281 String username = credentials.substring(0, idx); 283 284 if (expectingRealm.equals(WebDAVAuthenticationModule.DEFAULT_REALM)) { 285 AuthenticationScheme authScheme = (DefaultAuthenticationScheme) req.getSession() 286 .getAttribute(Constants.AUTH_SESSION); 287 if (authScheme == null) { 288 throw new Exception ("No authentication scheme initialised."); 289 } 290 291 User user = UserDatabaseManager.getInstance().getDefaultUserDatabase().getAccount(username); 293 authScheme.setUser(user); 294 LogonAction.authenticate(authScheme, req); 295 LogonAction.finishAuthentication(authScheme, req, res); 296 } else { 297 298 if (log.isDebugEnabled()) 299 log.debug("Logging " + username 300 + " [" 301 + req.getRemoteHost() 302 + "] onto realm " 303 + expectingRealm 304 + " using Basic authentication for session " 305 + req.getSession().getId()); 306 req.getSession().removeAttribute(ATTR_EXPECTING_REALM_AUTHENTICATION); 309 } 310 req.getSession().removeAttribute(Constants.AUTH_SENT); 311 312 313 if (log.isDebugEnabled()) 314 log.debug(req.getMethod() + ' ' + req.getRequestURI() + ' ' + req.getProtocol()); 315 316 sessionInfo = LogonControllerFactory.getInstance().getSessionInfo(req); 318 319 configureFromRequest(); 320 return true; 321 322 } 323 } catch (InvalidLoginCredentialsException ex) { 324 log.error(ex); 325 WebDAVAuthenticationModule.sendAuthorizationError(req, res, expectingRealm); 326 return false; 327 } catch (AccountLockedException ex) { 328 log.error(ex); 329 res.sendError(DAVStatus.SC_FORBIDDEN, ex.getMessage()); 330 return false; 331 } catch (Exception ex) { 332 log.error("Unexpected error", ex); 333 res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 334 return false; 335 } 336 } 337 338 public HttpServletResponse getResponse() { 339 return (HttpServletResponse ) res; 340 } 341 342 343 344 345 346 351 public HttpServletRequest getRequest() { 352 return req; 353 } 354 355 360 public String getMethod() { 361 return this.req.getMethod(); 362 } 363 364 371 public String getPath() { 372 return path; 373 } 381 382 public boolean isRequiredRootRedirect() { 383 return false; 384 } 385 386 391 public String getPathEncoded() { 392 return DAVUtilities.encodePath(getPath()); 393 } 394 395 400 public int getDepth() { 401 String depth = req.getHeader("Depth"); 402 if (depth == null) 403 return INFINITY; 404 if ("infinity".equals(depth)) 405 return INFINITY; 406 try { 407 return Integer.parseInt(depth); 408 } catch (NumberFormatException exception) { 409 throw new DAVException(412, "Unable to parse depth", exception); 410 } 411 } 412 413 417 public URI getDestination() { 418 String destination = this.req.getHeader("Destination"); 419 if (destination != null) 420 try { 421 return this.base.relativize(new URI (destination.replaceAll(" ", "%20"))); 422 } catch (URISyntaxException exception) { 423 throw new DAVException(412, "Can't parse destination", exception); 424 } 425 return null; 426 } 427 428 433 public boolean getOverwrite() { 434 String overwrite = req.getHeader("Overwrite"); 435 if (overwrite == null) 436 return true; 437 if ("T".equals(overwrite)) 438 return true; 439 if ("F".equals(overwrite)) 440 return false; 441 throw new DAVException(412, "Unable to parse overwrite flag"); 442 } 443 444 449 public Date getIfModifiedSince() { 450 String name = "If-Modified-Since"; 451 if (this.req.getHeader(name) == null) 452 return null; 453 return new Date (this.req.getDateHeader(name)); 454 } 455 456 457 458 459 460 465 public void setStatus(int status) { 466 this.res.setStatus(status); 467 } 468 469 474 public void setContentType(String type) { 475 this.res.setContentType(type); 476 } 477 478 483 public void setHeader(String name, String value) { 484 this.res.setHeader(name, value); 485 } 486 487 495 public void setDateHeader(String name, int value) { 496 this.res.setDateHeader(name, value); 497 } 498 499 500 501 502 503 508 public InputStream getInputStream() throws IOException { 509 510 if (req.getHeader("Content-Range") != null) 511 throw new DAVException(501, "Content-Range not supported"); 512 513 if (this.req.getContentLength() >= 0) 514 this.req.getInputStream(); 515 String len = this.req.getHeader("Content-Length"); 516 if (len != null) 517 try { 518 if (Long.parseLong(len) > 0) 519 return this.req.getInputStream(); 520 } catch (NumberFormatException exception) { 521 } 523 524 return null; 527 } 528 529 534 public OutputStream getOutputStream() throws IOException { 535 if (System.getProperty("sslexplorer.webdav.debug", "false").equals("true")) 536 return new TempOutputStream(this.res.getOutputStream()); 537 else 538 return this.res.getOutputStream(); 539 } 540 541 class TempOutputStream extends OutputStream { 542 543 OutputStream out; 544 StringBuffer wbBuf; 545 546 TempOutputStream(OutputStream out) { 547 this.out = out; 548 wbBuf = new StringBuffer (); 549 } 550 551 public void write(byte[] buf, int off, int len) throws IOException { 552 wbBuf.append(new String (buf, off, len)); 553 out.write(buf, off, len); 554 } 555 556 public void write(int b) throws IOException { 557 wbBuf.append((byte) b); 558 out.write((byte) b); 559 } 560 561 public void flush() throws IOException { 562 log.info(wbBuf.toString()); 563 wbBuf.setLength(0); 564 out.flush(); 565 } 566 567 } 568 569 574 public PrintWriter write(String encoding) throws IOException { 575 return new PrintWriter (new OutputStreamWriter (this.getOutputStream(), encoding)); 576 } 577 578 579 580 581 582 588 public URI lookup(VFSResource resource) { 589 URI uri = resource.getRelativeURI(); 590 URI resolved = null; 591 if (uri == null || uri.toString().equals("")) { 592 resolved = this.base; 593 } else { 594 resolved = this.base.resolve(uri).normalize(); 595 ; 596 } 597 return resolved; 598 } 599 600 public PasswordCredentials getCredentials() { 601 String authorization = req.getHeader("Authorization"); 602 603 if (authorization == null) { 604 return null; 605 } 606 607 int idx = authorization.indexOf(' '); 608 609 if (idx == -1 || idx == authorization.length() - 1) { 610 return null; 611 } 612 613 String method = authorization.substring(0, idx); 615 616 if (!method.equalsIgnoreCase("basic")) { 617 return null; 618 } 619 620 String encoded = authorization.substring(idx + 1); 622 623 String credentials = new String (Base64.decode(encoded)); 624 idx = credentials.indexOf(':'); 625 626 if (idx == 0 || idx == -1) { 627 return null; 628 } 629 630 String username = credentials.substring(0, idx); 632 String password = credentials.substring(idx + 1); 633 634 return new PasswordCredentials(username, password.toCharArray()); 635 } 636 637 643 public SessionInfo getSessionInfo() { 644 return sessionInfo; 645 } 646 647 654 public boolean isResourcePath(String fullResourcePath) { 655 String fullUri = DAVUtilities.stripTrailingSlash(DAVUtilities.stripLeadingSlash(fullResourcePath)); 656 return fullUri.equals(getPath()); 657 } 658 } | Popular Tags |