/**
   * Perform access control based on the specified authorization constraint. Return <code>true
   * </code> if this constraint is satisfied and processing should continue, or <code>false</code>
   * otherwise.
   *
   * @param request Request we are processing
   * @param response Response we are creating
   * @param constraints Security constraint we are enforcing
   * @param context The Context to which client of this class is attached.
   * @exception IOException if an input/output error occurs
   */
  @Override
  public boolean hasResourcePermission(
      Request request, Response response, SecurityConstraint[] constraints, Context context)
      throws IOException {

    if (constraints == null || constraints.length == 0) return (true);

    // Specifically allow access to the form login and form error pages
    // and the "j_security_check" action
    LoginConfig config = context.getLoginConfig();
    if ((config != null) && (Constants.FORM_METHOD.equals(config.getAuthMethod()))) {
      String requestURI = request.getRequestPathMB().toString();
      String loginPage = config.getLoginPage();
      if (loginPage.equals(requestURI)) {
        if (log.isDebugEnabled()) log.debug(" Allow access to login page " + loginPage);
        return (true);
      }
      String errorPage = config.getErrorPage();
      if (errorPage.equals(requestURI)) {
        if (log.isDebugEnabled()) log.debug(" Allow access to error page " + errorPage);
        return (true);
      }
      if (requestURI.endsWith(Constants.FORM_ACTION)) {
        if (log.isDebugEnabled()) log.debug(" Allow access to username/password submission");
        return (true);
      }
    }

    // Which user principal have we already authenticated?
    Principal principal = request.getPrincipal();
    boolean status = false;
    boolean denyfromall = false;
    for (int i = 0; i < constraints.length; i++) {
      SecurityConstraint constraint = constraints[i];

      String roles[];
      if (constraint.getAllRoles()) {
        // * means all roles defined in web.xml
        roles = request.getContext().findSecurityRoles();
      } else {
        roles = constraint.findAuthRoles();
      }

      if (roles == null) roles = new String[0];

      if (log.isDebugEnabled()) log.debug("  Checking roles " + principal);

      if (roles.length == 0 && !constraint.getAllRoles()) {
        if (constraint.getAuthConstraint()) {
          if (log.isDebugEnabled()) log.debug("No roles");
          status = false; // No listed roles means no access at all
          denyfromall = true;
          break;
        }

        if (log.isDebugEnabled()) log.debug("Passing all access");
        status = true;
      } else if (principal == null) {
        if (log.isDebugEnabled()) log.debug("  No user authenticated, cannot grant access");
      } else {
        for (int j = 0; j < roles.length; j++) {
          if (hasRole(null, principal, roles[j])) {
            status = true;
            if (log.isDebugEnabled()) log.debug("Role found:  " + roles[j]);
          } else if (log.isDebugEnabled()) log.debug("No role found:  " + roles[j]);
        }
      }
    }

    if (!denyfromall && allRolesMode != AllRolesMode.STRICT_MODE && !status && principal != null) {
      if (log.isDebugEnabled()) {
        log.debug("Checking for all roles mode: " + allRolesMode);
      }
      // Check for an all roles(role-name="*")
      for (int i = 0; i < constraints.length; i++) {
        SecurityConstraint constraint = constraints[i];
        String roles[];
        // If the all roles mode exists, sets
        if (constraint.getAllRoles()) {
          if (allRolesMode == AllRolesMode.AUTH_ONLY_MODE) {
            if (log.isDebugEnabled()) {
              log.debug("Granting access for role-name=*, auth-only");
            }
            status = true;
            break;
          }

          // For AllRolesMode.STRICT_AUTH_ONLY_MODE there must be zero roles
          roles = request.getContext().findSecurityRoles();
          if (roles.length == 0 && allRolesMode == AllRolesMode.STRICT_AUTH_ONLY_MODE) {
            if (log.isDebugEnabled()) {
              log.debug("Granting access for role-name=*, strict auth-only");
            }
            status = true;
            break;
          }
        }
      }
    }

    // Return a "Forbidden" message denying access to this resource
    if (!status) {
      response.sendError(HttpServletResponse.SC_FORBIDDEN, sm.getString("realmBase.forbidden"));
    }
    return status;
  }
  /**
   * Return the SecurityConstraints configured to guard the request URI for this request, or <code>
   * null</code> if there is no such constraint.
   *
   * @param request Request we are processing
   * @param context Context the Request is mapped to
   */
  @Override
  public SecurityConstraint[] findSecurityConstraints(Request request, Context context) {

    ArrayList<SecurityConstraint> results = null;
    // Are there any defined security constraints?
    SecurityConstraint constraints[] = context.findConstraints();
    if ((constraints == null) || (constraints.length == 0)) {
      if (log.isDebugEnabled()) log.debug("  No applicable constraints defined");
      return (null);
    }

    // Check each defined security constraint
    String uri = request.getRequestPathMB().toString();
    // Bug47080 - in rare cases this may be null
    // Mapper treats as '/' do the same to prevent NPE
    if (uri == null) {
      uri = "/";
    }

    String method = request.getMethod();
    int i;
    boolean found = false;
    for (i = 0; i < constraints.length; i++) {
      SecurityCollection[] collection = constraints[i].findCollections();

      // If collection is null, continue to avoid an NPE
      // See Bugzilla 30624
      if (collection == null) {
        continue;
      }

      if (log.isDebugEnabled()) {
        log.debug(
            "  Checking constraint '"
                + constraints[i]
                + "' against "
                + method
                + " "
                + uri
                + " --> "
                + constraints[i].included(uri, method));
      }

      for (int j = 0; j < collection.length; j++) {
        String[] patterns = collection[j].findPatterns();

        // If patterns is null, continue to avoid an NPE
        // See Bugzilla 30624
        if (patterns == null) {
          continue;
        }

        for (int k = 0; k < patterns.length; k++) {
          if (uri.equals(patterns[k])) {
            found = true;
            if (collection[j].findMethod(method)) {
              if (results == null) {
                results = new ArrayList<SecurityConstraint>();
              }
              results.add(constraints[i]);
            }
          }
        }
      }
    }

    if (found) {
      return resultsToArray(results);
    }

    int longest = -1;

    for (i = 0; i < constraints.length; i++) {
      SecurityCollection[] collection = constraints[i].findCollections();

      // If collection is null, continue to avoid an NPE
      // See Bugzilla 30624
      if (collection == null) {
        continue;
      }

      if (log.isDebugEnabled()) {
        log.debug(
            "  Checking constraint '"
                + constraints[i]
                + "' against "
                + method
                + " "
                + uri
                + " --> "
                + constraints[i].included(uri, method));
      }

      for (int j = 0; j < collection.length; j++) {
        String[] patterns = collection[j].findPatterns();

        // If patterns is null, continue to avoid an NPE
        // See Bugzilla 30624
        if (patterns == null) {
          continue;
        }

        boolean matched = false;
        int length = -1;
        for (int k = 0; k < patterns.length; k++) {
          String pattern = patterns[k];
          if (pattern.startsWith("/") && pattern.endsWith("/*") && pattern.length() >= longest) {

            if (pattern.length() == 2) {
              matched = true;
              length = pattern.length();
            } else if (pattern.regionMatches(0, uri, 0, pattern.length() - 1)
                || (pattern.length() - 2 == uri.length()
                    && pattern.regionMatches(0, uri, 0, pattern.length() - 2))) {
              matched = true;
              length = pattern.length();
            }
          }
        }
        if (matched) {
          found = true;
          if (length > longest) {
            if (results != null) {
              results.clear();
            }
            longest = length;
          }
          if (collection[j].findMethod(method)) {
            if (results == null) {
              results = new ArrayList<SecurityConstraint>();
            }
            results.add(constraints[i]);
          }
        }
      }
    }

    if (found) {
      return resultsToArray(results);
    }

    for (i = 0; i < constraints.length; i++) {
      SecurityCollection[] collection = constraints[i].findCollections();

      // If collection is null, continue to avoid an NPE
      // See Bugzilla 30624
      if (collection == null) {
        continue;
      }

      if (log.isDebugEnabled()) {
        log.debug(
            "  Checking constraint '"
                + constraints[i]
                + "' against "
                + method
                + " "
                + uri
                + " --> "
                + constraints[i].included(uri, method));
      }

      boolean matched = false;
      int pos = -1;
      for (int j = 0; j < collection.length; j++) {
        String[] patterns = collection[j].findPatterns();

        // If patterns is null, continue to avoid an NPE
        // See Bugzilla 30624
        if (patterns == null) {
          continue;
        }

        for (int k = 0; k < patterns.length && !matched; k++) {
          String pattern = patterns[k];
          if (pattern.startsWith("*.")) {
            int slash = uri.lastIndexOf("/");
            int dot = uri.lastIndexOf(".");
            if (slash >= 0
                && dot > slash
                && dot != uri.length() - 1
                && uri.length() - dot == pattern.length() - 1) {
              if (pattern.regionMatches(1, uri, dot, uri.length() - dot)) {
                matched = true;
                pos = j;
              }
            }
          }
        }
      }
      if (matched) {
        found = true;
        if (collection[pos].findMethod(method)) {
          if (results == null) {
            results = new ArrayList<SecurityConstraint>();
          }
          results.add(constraints[i]);
        }
      }
    }

    if (found) {
      return resultsToArray(results);
    }

    for (i = 0; i < constraints.length; i++) {
      SecurityCollection[] collection = constraints[i].findCollections();

      // If collection is null, continue to avoid an NPE
      // See Bugzilla 30624
      if (collection == null) {
        continue;
      }

      if (log.isDebugEnabled()) {
        log.debug(
            "  Checking constraint '"
                + constraints[i]
                + "' against "
                + method
                + " "
                + uri
                + " --> "
                + constraints[i].included(uri, method));
      }

      for (int j = 0; j < collection.length; j++) {
        String[] patterns = collection[j].findPatterns();

        // If patterns is null, continue to avoid an NPE
        // See Bugzilla 30624
        if (patterns == null) {
          continue;
        }

        boolean matched = false;
        for (int k = 0; k < patterns.length && !matched; k++) {
          String pattern = patterns[k];
          if (pattern.equals("/")) {
            matched = true;
          }
        }
        if (matched) {
          if (results == null) {
            results = new ArrayList<SecurityConstraint>();
          }
          results.add(constraints[i]);
        }
      }
    }

    if (results == null) {
      // No applicable security constraint was found
      if (log.isDebugEnabled()) log.debug("  No applicable constraint located");
    }
    return resultsToArray(results);
  }