com.microsoft.exchange.autodiscover.AutodiscoverRedirectStrategy.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.exchange.autodiscover.AutodiscoverRedirectStrategy.java

Source

/**
 * See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Board of Regents of the University of Wisconsin System
 * 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 com.microsoft.exchange.autodiscover;

import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.ProtocolException;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.protocol.HttpContext;

import com.microsoft.exchange.exception.ExchangeWebServicesRuntimeException;

/**
* 
* BLATANTLY STOLEN FROM https://github.com/Jasig/email-preview
* 
* Redirect strategy for http client that allows following redirects (HTTP 301, 302, 307) for POST messages after
* validating the redirect location is "safe".
*
* This class does not limit redirect hops; DefaultHttpClient limits redirect hops to 100 (see
* ClientPNames.MAX_REDIRECTS ='http.protocol.max-redirects' in
* http://hc.apache.org/httpcomponents-client-ga/tutorial/pdf/httpclient-tutorial.pdf.
*
* @author James Wennmacher, jwennmacher@unicon.net
*/

public class AutodiscoverRedirectStrategy extends LaxRedirectStrategy {

    protected final Log log = LogFactory.getLog(this.getClass());

    private List<String> unsafeUriExclusionPatterns;
    private List<Pattern> unsafeUriPatterns = new ArrayList<Pattern>();
    private List<String> requiredUriPatterns;
    private List<Pattern> uriRequirementPatterns = new ArrayList<Pattern>();

    public AutodiscoverRedirectStrategy() {
        setRequiredUriPatterns(Arrays.asList(new String[] { "^https:.*" }));
    }

    public void setUnsafeUriExclusionPatterns(List<String> unsafeUriExclusionPatterns) {
        this.unsafeUriExclusionPatterns = unsafeUriExclusionPatterns;
        unsafeUriPatterns = new ArrayList<Pattern>();
        for (String pattern : unsafeUriExclusionPatterns) {
            unsafeUriPatterns.add(Pattern.compile(pattern));
        }
    }

    public void setRequiredUriPatterns(List<String> requiredUriPatterns) {
        this.requiredUriPatterns = requiredUriPatterns;
        uriRequirementPatterns = new ArrayList<Pattern>();
        for (String pattern : requiredUriPatterns) {
            uriRequirementPatterns.add(Pattern.compile(pattern));
        }
    }

    private boolean matchesPatternSet(URI uri, List<Pattern> patterns) {
        for (Pattern pattern : patterns) {
            Matcher matcher = pattern.matcher(uri.toString());
            if (matcher.matches()) {
                return true;
            }
        }
        return false;
    }

    /**
    * Overrides behavior to follow redirects for POST messages, AND to have the redirect be a POST.  Behavior of
    * <code>DefaultRedirectStrategy</code> is to use a GET for the redirect (though against spec this is the
    * de-facto standard, see http://www.mail-archive.com/httpclient-users@hc.apache.org/msg06327.html and
    * http://www.alanflavell.org.uk/www/post-redirect.html).
    *
    * For our application, we want to follow the redirect for a 302 as long as it is to a safe location and
    * have the redirect be a POST.
    *
    * This code is modified from http-components' http-client 4.2.5.  Since we only use POST the code for the
    * other HTTP methods has been removed to simplify this method.
    *
    * @param request Http request
    * @param response Http response
    * @param context Http context
    * @return Request to issue to the redirected location
    * @throws ProtocolException protocol exception
    */
    @Override
    public HttpUriRequest getRedirect(final HttpRequest request, final HttpResponse response,
            final HttpContext context) throws ProtocolException {
        URI uri = getLocationURI(request, response, context);
        log.info("Following redirect to " + uri.toString());
        String method = request.getRequestLine().getMethod();
        int status = response.getStatusLine().getStatusCode();

        // Insure location is safe
        if (matchesPatternSet(uri, unsafeUriPatterns)) {
            log.warn("Not following to URI {} - matches a configured unsafe URI pattern " + uri.toString());
            throw new ExchangeWebServicesRuntimeException(
                    "Autodiscover redirected to unsafe URI " + uri.toString());
        }

        if (!matchesPatternSet(uri, uriRequirementPatterns) && uriRequirementPatterns.size() > 0) {
            log.warn("Not following to URI {} - URI does not match a required URI pattern " + uri.toString());
            throw new ExchangeWebServicesRuntimeException(
                    "Autodiscover redirected to URI not matching required pattern. URI=" + uri.toString());
        }

        // Follow forwards for 301 and 302 in addition to 307, to validate the redirect location,
        // and to use a POST method.
        if (status == HttpStatus.SC_TEMPORARY_REDIRECT || status == HttpStatus.SC_MOVED_PERMANENTLY
                || status == HttpStatus.SC_MOVED_TEMPORARILY) {
            if (method.equalsIgnoreCase(HttpPost.METHOD_NAME)) {
                return copyEntity(new HttpPost(uri), request);
            }
        }

        // Should not get here, but return sensible value just in case.  A GET will likely fail.
        return new HttpGet(uri);
    }

    private HttpUriRequest copyEntity(final HttpEntityEnclosingRequestBase redirect, final HttpRequest original) {
        if (original instanceof HttpEntityEnclosingRequest) {
            redirect.setEntity(((HttpEntityEnclosingRequest) original).getEntity());
        }
        return redirect;
    }

}