| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.catalina.valves; |
| |
| |
| import java.io.IOException; |
| import java.util.regex.Pattern; |
| |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.catalina.Context; |
| import org.apache.catalina.LifecycleException; |
| import org.apache.catalina.connector.Request; |
| import org.apache.catalina.connector.Response; |
| import org.apache.juli.logging.Log; |
| |
| /** |
| * Implementation of a Valve that performs filtering based on comparing the |
| * appropriate request property (selected based on which subclass you choose |
| * to configure into your Container's pipeline) against the regular expressions |
| * configured for this Valve. |
| * <p> |
| * This valve is configured by setting the <code>allow</code> and/or |
| * <code>deny</code> properties to a regular expressions (in the syntax |
| * supported by {@link Pattern}) to which the appropriate request property will |
| * be compared. Evaluation proceeds as follows: |
| * <ul> |
| * <li>The subclass extracts the request property to be filtered, and |
| * calls the common <code>process()</code> method. |
| * <li>If there is a deny expression configured, the property will be compared |
| * to the expression. If a match is found, this request will be rejected |
| * with a "Forbidden" HTTP response.</li> |
| * <li>If there is a allow expression configured, the property will be compared |
| * to each such expression. If a match is found, this request will be |
| * allowed to pass through to the next Valve in the current pipeline.</li> |
| * <li>If a deny expression was specified but no allow expression, allow this |
| * request to pass through (because none of the deny expressions matched |
| * it). |
| * <li>The request will be rejected with a "Forbidden" HTTP response.</li> |
| * </ul> |
| * <p> |
| * As an option the valve can generate an invalid <code>authenticate</code> |
| * header instead of denying the request. This can be combined with the |
| * context attribute <code>preemptiveAuthentication="true"</code> and an |
| * authenticator to force authentication instead of denial. |
| * <p> |
| * This Valve may be attached to any Container, depending on the granularity |
| * of the filtering you wish to perform. |
| * |
| * @author Craig R. McClanahan |
| */ |
| public abstract class RequestFilterValve extends ValveBase { |
| |
| //------------------------------------------------------ Constructor |
| public RequestFilterValve() { |
| super(true); |
| } |
| |
| |
| // ----------------------------------------------------- Instance Variables |
| |
| /** |
| * The regular expression used to test for allowed requests. |
| */ |
| protected volatile Pattern allow = null; |
| |
| |
| /** |
| * The current allow configuration value that may or may not compile into a |
| * valid {@link Pattern}. |
| */ |
| protected volatile String allowValue = null; |
| |
| |
| /** |
| * Helper variable to catch configuration errors. |
| * It is <code>true</code> by default, but becomes <code>false</code> |
| * if there was an attempt to assign an invalid value to the |
| * <code>allow</code> pattern. |
| */ |
| protected volatile boolean allowValid = true; |
| |
| |
| /** |
| * The regular expression used to test for denied requests. |
| */ |
| protected volatile Pattern deny = null; |
| |
| |
| /** |
| * The current deny configuration value that may or may not compile into a |
| * valid {@link Pattern}. |
| */ |
| protected volatile String denyValue = null; |
| |
| |
| /** |
| * Helper variable to catch configuration errors. |
| * It is <code>true</code> by default, but becomes <code>false</code> |
| * if there was an attempt to assign an invalid value to the |
| * <code>deny</code> pattern. |
| */ |
| protected volatile boolean denyValid = true; |
| |
| |
| /** |
| * The HTTP response status code that is used when rejecting denied |
| * request. It is 403 by default, but may be changed to be 404. |
| */ |
| protected int denyStatus = HttpServletResponse.SC_FORBIDDEN; |
| |
| /** |
| * <p>If <code>invalidAuthenticationWhenDeny</code> is true |
| * and the context has <code>preemptiveAuthentication</code> |
| * set, set an invalid authorization header to trigger basic auth |
| * instead of denying the request.. |
| */ |
| private boolean invalidAuthenticationWhenDeny = false; |
| |
| // ------------------------------------------------------------- Properties |
| |
| |
| /** |
| * Return the regular expression used to test for allowed requests for this |
| * Valve, if any; otherwise, return <code>null</code>. |
| */ |
| public String getAllow() { |
| return allowValue; |
| } |
| |
| |
| /** |
| * Set the regular expression used to test for allowed requests for this |
| * Valve, if any. |
| * |
| * @param allow The new allow expression |
| */ |
| public void setAllow(String allow) { |
| if (allow == null || allow.length() == 0) { |
| this.allow = null; |
| allowValue = null; |
| allowValid = true; |
| } else { |
| boolean success = false; |
| try { |
| allowValue = allow; |
| this.allow = Pattern.compile(allow); |
| success = true; |
| } finally { |
| allowValid = success; |
| } |
| } |
| } |
| |
| |
| /** |
| * Return the regular expression used to test for denied requests for this |
| * Valve, if any; otherwise, return <code>null</code>. |
| */ |
| public String getDeny() { |
| return denyValue; |
| } |
| |
| |
| /** |
| * Set the regular expression used to test for denied requests for this |
| * Valve, if any. |
| * |
| * @param deny The new deny expression |
| */ |
| public void setDeny(String deny) { |
| if (deny == null || deny.length() == 0) { |
| this.deny = null; |
| denyValue = null; |
| denyValid = true; |
| } else { |
| boolean success = false; |
| try { |
| denyValue = deny; |
| this.deny = Pattern.compile(deny); |
| success = true; |
| } finally { |
| denyValid = success; |
| } |
| } |
| } |
| |
| |
| /** |
| * Returns {@code false} if the last change to the {@code allow} pattern did |
| * not apply successfully. E.g. if the pattern is syntactically |
| * invalid. |
| */ |
| public final boolean isAllowValid() { |
| return allowValid; |
| } |
| |
| |
| /** |
| * Returns {@code false} if the last change to the {@code deny} pattern did |
| * not apply successfully. E.g. if the pattern is syntactically |
| * invalid. |
| */ |
| public final boolean isDenyValid() { |
| return denyValid; |
| } |
| |
| |
| /** |
| * Return response status code that is used to reject denied request. |
| */ |
| public int getDenyStatus() { |
| return denyStatus; |
| } |
| |
| |
| /** |
| * Set response status code that is used to reject denied request. |
| */ |
| public void setDenyStatus(int denyStatus) { |
| this.denyStatus = denyStatus; |
| } |
| |
| |
| /** |
| * Return true if a deny is handled by setting an invalid auth header. |
| */ |
| public boolean getInvalidAuthenticationWhenDeny() { |
| return invalidAuthenticationWhenDeny; |
| } |
| |
| |
| /** |
| * Set invalidAuthenticationWhenDeny property. |
| */ |
| public void setInvalidAuthenticationWhenDeny(boolean value) { |
| invalidAuthenticationWhenDeny = value; |
| } |
| |
| |
| // --------------------------------------------------------- Public Methods |
| |
| /** |
| * Extract the desired request property, and pass it (along with the |
| * specified request and response objects) to the protected |
| * <code>process()</code> method to perform the actual filtering. |
| * This method must be implemented by a concrete subclass. |
| * |
| * @param request The servlet request to be processed |
| * @param response The servlet response to be created |
| * |
| * @exception IOException if an input/output error occurs |
| * @exception ServletException if a servlet error occurs |
| */ |
| @Override |
| public abstract void invoke(Request request, Response response) |
| throws IOException, ServletException; |
| |
| |
| // ------------------------------------------------------ Protected Methods |
| |
| |
| @Override |
| protected void initInternal() throws LifecycleException { |
| super.initInternal(); |
| if (!allowValid || !denyValid) { |
| throw new LifecycleException( |
| sm.getString("requestFilterValve.configInvalid")); |
| } |
| } |
| |
| |
| @Override |
| protected synchronized void startInternal() throws LifecycleException { |
| if (!allowValid || !denyValid) { |
| throw new LifecycleException( |
| sm.getString("requestFilterValve.configInvalid")); |
| } |
| super.startInternal(); |
| } |
| |
| |
| /** |
| * Perform the filtering that has been configured for this Valve, matching |
| * against the specified request property. |
| * |
| * @param property The request property on which to filter |
| * @param request The servlet request to be processed |
| * @param response The servlet response to be processed |
| * |
| * @exception IOException if an input/output error occurs |
| * @exception ServletException if a servlet error occurs |
| */ |
| protected void process(String property, Request request, Response response) |
| throws IOException, ServletException { |
| |
| if (isAllowed(property)) { |
| getNext().invoke(request, response); |
| return; |
| } |
| |
| if (getLog().isDebugEnabled()) { |
| getLog().debug(sm.getString("requestFilterValve.deny", |
| request.getRequestURI(), property)); |
| } |
| |
| // Deny this request |
| denyRequest(request, response); |
| } |
| |
| |
| protected abstract Log getLog(); |
| |
| |
| /** |
| * Reject the request that was denied by this valve. |
| * <p>If <code>invalidAuthenticationWhenDeny</code> is true |
| * and the context has <code>preemptiveAuthentication</code> |
| * set, set an invalid authorization header to trigger basic auth. |
| * |
| * @param request The servlet request to be processed |
| * @param response The servlet response to be processed |
| * @exception IOException if an input/output error occurs |
| * @exception ServletException if a servlet error occurs |
| */ |
| protected void denyRequest(Request request, Response response) |
| throws IOException, ServletException { |
| if (invalidAuthenticationWhenDeny) { |
| Context context = request.getContext(); |
| if (context != null && context.getPreemptiveAuthentication()) { |
| if (request.getCoyoteRequest().getMimeHeaders().getValue("authorization") == null) { |
| request.getCoyoteRequest().getMimeHeaders().addValue("authorization").setString("invalid"); |
| } |
| getNext().invoke(request, response); |
| return; |
| } |
| } |
| response.sendError(denyStatus); |
| } |
| |
| |
| /** |
| * Perform the test implemented by this Valve, matching against the |
| * specified request property value. This method is public so that it can be |
| * called through JMX, e.g. to test whether certain IP address is allowed or |
| * denied by the valve configuration. |
| * |
| * @param property |
| * The request property value on which to filter |
| */ |
| public boolean isAllowed(String property) { |
| // Use local copies for thread safety |
| Pattern deny = this.deny; |
| Pattern allow = this.allow; |
| |
| // Check the deny patterns, if any |
| if (deny != null && deny.matcher(property).matches()) { |
| return false; |
| } |
| |
| // Check the allow patterns, if any |
| if (allow != null && allow.matcher(property).matches()) { |
| return true; |
| } |
| |
| // Allow if denies specified but not allows |
| if (deny != null && allow == null) { |
| return true; |
| } |
| |
| // Deny this request |
| return false; |
| } |
| } |