Package hirondelle.web4j.security

Source Code of hirondelle.web4j.security.CsrfModifiedResponse

package hirondelle.web4j.security;

import hirondelle.web4j.model.Id;
import hirondelle.web4j.util.EscapeChars;
import hirondelle.web4j.util.Regex;
import hirondelle.web4j.util.Util;

import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/** Add a nonce to POSTed forms in any 'text/html' response. */
final class CsrfModifiedResponse {

  CsrfModifiedResponse(HttpServletRequest aRequest, HttpServletResponse aResponse){
    fResponse = aResponse;
    fRequest = aRequest;
  }
 
  String addNonceTo(String aUnmodifiedResponse){
    String result = aUnmodifiedResponse;
    if(isServingHtml() && Util.textHasContent(aUnmodifiedResponse))  {
      fLogger.fine("Adding nonce to forms having method=POST, if any.");
      result = addHiddenParamToPostedForms(aUnmodifiedResponse);
    }
    return result;
  }
 
  // PRIVATE
 
  private HttpServletRequest fRequest;
  private HttpServletResponse fResponse;

  /**
    Group 1 is the FORM start tag *plus the body*, and group 2 is the FORM end tag.
    Note the non-greedy qualifier for group 2, to ensure multiple forms are not glommed together.
  */
  private static final String REGEX =
     "(<form" + Regex.ALL_BUT_END_OF_TAG +"method=" + Regex.QUOTE + "POST" + Regex.QUOTE + Regex.ALL_BUT_END_OF_TAG + ">" 
     +  Regex.ANY_CHARS + "?)" +
     "(</form>)"
  ;
  private static final Pattern FORM_PATTERN = Pattern.compile(REGEX, Pattern.DOTALL | Pattern.CASE_INSENSITIVE);

  private static final String TEXT_HTML = "text/html";
 
  /**
   Problem: this class is package private. If we use the 'regular' logger, named for this class, then the output will not
   show up. So, we use a logger attached to a closely related, public class. (Neat!)
  */
  private static final Logger fLogger = Util.getLogger(CsrfFilter.class);

  /** Return true if content-type of reponse is null, or starts with 'text/html' (case-sensitive).  */
  private boolean isServingHtml(){
    String contentType = fResponse.getContentType();
    boolean missingContentType = ! Util.textHasContent(contentType);
    boolean startsWithHTML = Util.textHasContent(contentType) && contentType.startsWith(TEXT_HTML);
    return missingContentType || startsWithHTML;
  }
 
  private String addHiddenParamToPostedForms(String aOriginalInput) {
    StringBuffer result = new StringBuffer();
    Matcher formMatcher = FORM_PATTERN.matcher(aOriginalInput);
    while ( formMatcher.find() ){
      fLogger.fine("Found a POSTed form. Adding nonce.");
      formMatcher.appendReplacement(result, getReplacement(formMatcher));
    }
    formMatcher.appendTail(result);
    return result.toString();
  }
 
  private String getReplacement(Matcher aMatcher){
    //escape, since '$' char may appear in input
    return EscapeChars.forReplacementString(aMatcher.group(1) +  getHiddenInputTag() +  aMatcher.group(2));
  }
 
  private String getHiddenInputTag(){
    return "<input type='hidden' name='" + getHiddenParamName() + "' value='" + getHiddenParamValue().toString() + "'>";
  }
 
  /**
   Return the form-source id value, stored in the user's session.
   If there is no session, or if there is no form-source id in the session, throw a RuntimeException. 
  */
  private Id getHiddenParamValue(){
    Id result = null;
    boolean DO_NOT_CREATE = false;
    HttpSession session = fRequest.getSession(DO_NOT_CREATE);
    if ( session != null ) {
      result = (Id)session.getAttribute(CsrfFilter.FORM_SOURCE_ID_KEY);
      if( result == null ){
        String message =  "Session exists, but no CSRF token value is stored in the session";
        fLogger.severe(message);
        throw new RuntimeException(message);
      }
    }
    else {
      String message =
        "No session exists! CsrfFilter can only work when a session is present, and the user has logged in. " +
        "Ensure CsrfFilter is mapped (using url-pattern) only to URLs having mandatory login and/or a valid session."
      ;
      fLogger.severe(message);
      throw new RuntimeException(message);
    }
    return result;
  }
 
  /** Return the name of the hidden form parameter.  */
  private String getHiddenParamName(){
    return CsrfFilter.FORM_SOURCE_ID_KEY;
  }
}
TOP

Related Classes of hirondelle.web4j.security.CsrfModifiedResponse

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.