001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2012-2014 ForgeRock AS.
015 */
016
017package org.forgerock.openig.filter;
018
019import static org.forgerock.openig.util.Json.*;
020
021import java.io.IOException;
022import java.net.URI;
023import java.net.URISyntaxException;
024
025import org.forgerock.openig.el.Expression;
026import org.forgerock.openig.handler.Handler;
027import org.forgerock.openig.handler.HandlerException;
028import org.forgerock.openig.header.LocationHeader;
029import org.forgerock.openig.heap.GenericHeaplet;
030import org.forgerock.openig.heap.HeapException;
031import org.forgerock.openig.http.Exchange;
032import org.forgerock.openig.http.Message;
033import org.forgerock.openig.util.URIUtil;
034
035/**
036 * Rewrites Location headers on responses that generate a redirect that would
037 * take the user directly to the application being proxied rather than taking
038 * the user through OpenIG.
039 */
040public class LocationHeaderFilter extends GenericFilter {
041
042    /** The base URI of the OpenIG instance, used to rewrite Location headers. */
043    private Expression baseURI;
044
045    /**
046     * Sets the base URI used to rewrite Location headers.
047     * @param baseURI expression that, when evaluated, will represents the base URI of this OpenIG instance
048     */
049    public void setBaseURI(final Expression baseURI) {
050        this.baseURI = baseURI;
051    }
052
053    @Override
054    public void filter(Exchange exchange, Handler next) throws HandlerException, IOException {
055        // We only care about responses so just call the next handler in the chain.
056        next.handle(exchange);
057
058        processResponse(exchange);
059    }
060
061    /**
062     * Rewrite Location header if it would have the user go directly to the application.
063     *
064     * @param exchange the exchange containing the response message containing the Location header
065     */
066    private void processResponse(Exchange exchange) throws HandlerException {
067        Message<?> message = exchange.response;
068        LocationHeader header = new LocationHeader(message);
069        if (header.toString() != null) {
070            try {
071                URI currentURI = new URI(header.toString());
072                URI rebasedURI = URIUtil.rebase(currentURI, evaluateBaseUri(exchange));
073                // Only rewrite header if it has changed
074                if (!currentURI.equals(rebasedURI)) {
075                    message.getHeaders().remove(LocationHeader.NAME);
076                    message.getHeaders().add(LocationHeader.NAME, rebasedURI.toString());
077                }
078            } catch (URISyntaxException ex) {
079                throw logger.debug(new HandlerException(ex));
080            }
081        }
082    }
083
084    private URI evaluateBaseUri(final Exchange exchange) throws URISyntaxException, HandlerException {
085        String uri = baseURI.eval(exchange, String.class);
086        if (uri == null) {
087            throw logger.debug(new HandlerException("Evaluated baseURI cannot be null"));
088        }
089        return new URI(uri);
090    }
091
092    /** Creates and initializes a LocationHeaderFilter in a heap environment. */
093    public static class Heaplet extends GenericHeaplet {
094        @Override
095        public Object create() throws HeapException {
096
097            LocationHeaderFilter filter = new LocationHeaderFilter();
098            filter.baseURI = asExpression(config.get("baseURI").required());
099
100            return filter;
101        }
102    }
103}