/*
* JBoss, Home of Professional Open Source
*
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
*
* Licensed 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.picketlink.identity.federation.bindings.tomcat.sp;
import org.apache.catalina.Session;
import org.apache.catalina.authenticator.Constants;
import org.apache.catalina.authenticator.SavedRequest;
import org.apache.catalina.connector.Request;
import org.apache.coyote.ActionCode;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.MimeHeaders;
import javax.servlet.http.Cookie;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
/**
* PLINK-344: Account Chooser At the Service Provider to enable redirection to the appropriate IDP Implementation for Apache Tomcat
* 7
*
* @author Anil Saldhana
* @since January 22, 2014
*/
public class AccountChooserValve extends AbstractAccountChooserValve {
/**
* Save the original request information into our session.
*
* Implementation from
* http://svn.apache.org/repos/asf/tomcat/tc7.0.x/tags/TOMCAT_7_0_50/java/org/apache/catalina/authenticator
* /FormAuthenticator.java
*
* @param request The request to be saved
* @param session The session to contain the saved information
* @throws java.io.IOException
*/
protected void saveRequest(Request request, Session session) throws IOException {
// Create and populate a SavedRequest object for this request
SavedRequest saved = new SavedRequest();
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
saved.addCookie(cookies[i]);
}
}
Enumeration<String> names = request.getHeaderNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
Enumeration<String> values = request.getHeaders(name);
while (values.hasMoreElements()) {
String value = values.nextElement();
saved.addHeader(name, value);
}
}
Enumeration<Locale> locales = request.getLocales();
while (locales.hasMoreElements()) {
Locale locale = locales.nextElement();
saved.addLocale(locale);
}
// May need to acknowledge a 100-continue expectation
request.getResponse().sendAcknowledgement();
ByteChunk body = new ByteChunk();
body.setLimit(request.getConnector().getMaxSavePostSize());
byte[] buffer = new byte[4096];
int bytesRead;
InputStream is = request.getInputStream();
while ((bytesRead = is.read(buffer)) >= 0) {
body.append(buffer, 0, bytesRead);
}
// Only save the request body if there is something to save
if (body.getLength() > 0) {
saved.setContentType(request.getContentType());
saved.setBody(body);
}
saved.setMethod(request.getMethod());
saved.setQueryString(request.getQueryString());
saved.setRequestURI(request.getRequestURI());
saved.setDecodedRequestURI(request.getDecodedRequestURI());
// Stash the SavedRequest in our session for later use
session.setNote(Constants.FORM_REQUEST_NOTE, saved);
}
/**
* Restore the original request from information stored in our session. If the original request is no longer present
* (because the session timed out), return <code>false</code>; otherwise, return <code>true</code>.
*
* Implementation from
* http://svn.apache.org/repos/asf/tomcat/tc7.0.x/tags/TOMCAT_7_0_50/java/org/apache/catalina/authenticator
* /FormAuthenticator.java
*
* @param request The request to be restored
* @param session The session containing the saved information
*/
protected boolean restoreRequest(Request request, Session session) throws IOException {
// Retrieve and remove the SavedRequest object from our session
SavedRequest saved = (SavedRequest) session.getNote(Constants.FORM_REQUEST_NOTE);
session.removeNote(Constants.FORM_REQUEST_NOTE);
session.removeNote(Constants.FORM_PRINCIPAL_NOTE);
if (saved == null) {
return (false);
}
// Swallow any request body since we will be replacing it
// Need to do this before headers are restored as AJP connector uses
// content length header to determine how much data needs to be read for
// request body
byte[] buffer = new byte[4096];
InputStream is = request.createInputStream();
while (is.read(buffer) >= 0) {
// Ignore request body
}
// Modify our current request to reflect the original one
request.clearCookies();
Iterator<Cookie> cookies = saved.getCookies();
while (cookies.hasNext()) {
request.addCookie(cookies.next());
}
String method = saved.getMethod();
MimeHeaders rmh = request.getCoyoteRequest().getMimeHeaders();
rmh.recycle();
boolean cachable = "GET".equalsIgnoreCase(method) || "HEAD".equalsIgnoreCase(method);
Iterator<String> names = saved.getHeaderNames();
while (names.hasNext()) {
String name = names.next();
// The browser isn't expecting this conditional response now.
// Assuming that it can quietly recover from an unexpected 412.
// BZ 43687
if (!("If-Modified-Since".equalsIgnoreCase(name) || (cachable && "If-None-Match".equalsIgnoreCase(name)))) {
Iterator<String> values = saved.getHeaderValues(name);
while (values.hasNext()) {
rmh.addValue(name).setString(values.next());
}
}
}
request.clearLocales();
Iterator<Locale> locales = saved.getLocales();
while (locales.hasNext()) {
request.addLocale(locales.next());
}
request.getCoyoteRequest().getParameters().recycle();
request.getCoyoteRequest().getParameters().setQueryStringEncoding(request.getConnector().getURIEncoding());
ByteChunk body = saved.getBody();
if (body != null) {
request.getCoyoteRequest().action(ActionCode.REQ_SET_BODY_REPLAY, body);
// Set content type
MessageBytes contentType = MessageBytes.newInstance();
// If no content type specified, use default for POST
String savedContentType = saved.getContentType();
if (savedContentType == null && "POST".equalsIgnoreCase(method)) {
savedContentType = "application/x-www-form-urlencoded";
}
contentType.setString(savedContentType);
request.getCoyoteRequest().setContentType(contentType);
}
request.getCoyoteRequest().method().setString(method);
request.getCoyoteRequest().queryString().setString(saved.getQueryString());
request.getCoyoteRequest().requestURI().setString(saved.getRequestURI());
return (true);
}
}