001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.http;
019
020import java.io.File;
021import java.io.IOException;
022import java.net.HttpCookie;
023import java.net.HttpURLConnection;
024import java.net.URI;
025import java.net.URL;
026import java.security.GeneralSecurityException;
027import java.util.List;
028import javax.net.ssl.HttpsURLConnection;
029import javax.servlet.Filter;
030import javax.servlet.FilterChain;
031import javax.servlet.FilterConfig;
032import javax.servlet.ServletException;
033import javax.servlet.ServletRequest;
034import javax.servlet.ServletResponse;
035import javax.servlet.http.HttpServletResponse;
036import org.apache.hadoop.conf.Configuration;
037import org.apache.hadoop.fs.FileUtil;
038import org.apache.hadoop.hbase.HBaseClassTestRule;
039import org.apache.hadoop.hbase.testclassification.MiscTests;
040import org.apache.hadoop.hbase.testclassification.SmallTests;
041import org.apache.hadoop.net.NetUtils;
042import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
043import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
044import org.apache.hadoop.security.ssl.SSLFactory;
045import org.junit.AfterClass;
046import org.junit.Assert;
047import org.junit.BeforeClass;
048import org.junit.ClassRule;
049import org.junit.Test;
050import org.junit.experimental.categories.Category;
051
052@Category({ MiscTests.class, SmallTests.class })
053public class TestHttpCookieFlag {
054  @ClassRule
055  public static final HBaseClassTestRule CLASS_RULE =
056    HBaseClassTestRule.forClass(TestHttpCookieFlag.class);
057
058  private static final String BASEDIR = System.getProperty("test.build.dir", "target/test-dir")
059    + "/" + org.apache.hadoop.hbase.http.TestHttpCookieFlag.class.getSimpleName();
060  private static String keystoresDir;
061  private static String sslConfDir;
062  private static SSLFactory clientSslFactory;
063  private static HttpServer server;
064
065  public static class DummyAuthenticationFilter implements Filter {
066
067    @Override
068    public void init(FilterConfig filterConfig) throws ServletException {
069    }
070
071    @Override
072    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
073      throws IOException, ServletException {
074      HttpServletResponse resp = (HttpServletResponse) response;
075      boolean isHttps = "https".equals(request.getScheme());
076      AuthenticationFilter.createAuthCookie(resp, "token", null, null, -1, true, isHttps);
077      chain.doFilter(request, resp);
078    }
079
080    @Override
081    public void destroy() {
082    }
083  }
084
085  public static class DummyFilterInitializer extends FilterInitializer {
086    @Override
087    public void initFilter(FilterContainer container, Configuration conf) {
088      container.addFilter("DummyAuth", DummyAuthenticationFilter.class.getName(), null);
089    }
090  }
091
092  @BeforeClass
093  public static void setUp() throws Exception {
094    Configuration conf = new Configuration();
095    conf.set(HttpServer.FILTER_INITIALIZERS_PROPERTY, DummyFilterInitializer.class.getName());
096    conf.setInt("hbase.http.max.threads", 19); /* acceptors=2 + selectors=16 + request=1 */
097    System.setProperty("hadoop.log.dir", BASEDIR); /* needed for /logs */
098
099    File base = new File(BASEDIR);
100    FileUtil.fullyDelete(base);
101    base.mkdirs();
102    keystoresDir = new File(BASEDIR).getAbsolutePath();
103    sslConfDir = KeyStoreTestUtil.getClasspathDir(TestSSLHttpServer.class);
104
105    KeyStoreTestUtil.setupSSLConfig(keystoresDir, sslConfDir, conf, false);
106    Configuration sslConf = KeyStoreTestUtil.getSslConfig();
107
108    clientSslFactory = new SSLFactory(SSLFactory.Mode.CLIENT, sslConf);
109    clientSslFactory.init();
110
111    server = new HttpServer.Builder().setName("test").addEndpoint(new URI("http://localhost"))
112      .addEndpoint(new URI("https://localhost")).setConf(conf)
113      .keyPassword(sslConf.get("ssl.server.keystore.keypassword"))
114      .keyStore(sslConf.get("ssl.server.keystore.location"),
115        sslConf.get("ssl.server.keystore.password"), sslConf.get("ssl.server.keystore.type", "jks"))
116      .trustStore(sslConf.get("ssl.server.truststore.location"),
117        sslConf.get("ssl.server.truststore.password"),
118        sslConf.get("ssl.server.truststore.type", "jks"))
119      .build();
120    server.addPrivilegedServlet("echo", "/echo", TestHttpServer.EchoServlet.class);
121    server.start();
122  }
123
124  @Test
125  public void testHttpCookie() throws IOException {
126    URL base = new URL("http://" + NetUtils.getHostPortString(server.getConnectorAddress(0)));
127    HttpURLConnection conn = (HttpURLConnection) new URL(base, "/echo").openConnection();
128
129    String header = conn.getHeaderField("Set-Cookie");
130    Assert.assertTrue(header != null);
131    List<HttpCookie> cookies = HttpCookie.parse(header);
132    Assert.assertTrue(!cookies.isEmpty());
133    Assert.assertTrue(header.contains("; HttpOnly"));
134    Assert.assertTrue("token".equals(cookies.get(0).getValue()));
135  }
136
137  @Test
138  public void testHttpsCookie() throws IOException, GeneralSecurityException {
139    URL base = new URL("https://" + NetUtils.getHostPortString(server.getConnectorAddress(1)));
140    HttpsURLConnection conn = (HttpsURLConnection) new URL(base, "/echo").openConnection();
141    conn.setSSLSocketFactory(clientSslFactory.createSSLSocketFactory());
142
143    String header = conn.getHeaderField("Set-Cookie");
144    Assert.assertTrue(header != null);
145
146    List<HttpCookie> cookies = HttpCookie.parse(header);
147    Assert.assertTrue(!cookies.isEmpty());
148    Assert.assertTrue(header.contains("; HttpOnly"));
149    Assert.assertTrue(cookies.get(0).getSecure());
150    Assert.assertTrue("token".equals(cookies.get(0).getValue()));
151  }
152
153  @Test
154  public void testHttpsCookieDefaultServlets() throws Exception {
155    HttpsURLConnection conn = null;
156
157    URL base =
158      new URL("https://" + NetUtils.getHostPortString(server.getConnectorAddress(1)) + "/");
159
160    for (String servlet : new String[] { "static", "stacks", "logLevel", "jmx", "logs" }) {
161      conn = (HttpsURLConnection) new URL(base, "/" + servlet).openConnection();
162      conn.setSSLSocketFactory(clientSslFactory.createSSLSocketFactory());
163
164      String header = conn.getHeaderField("Set-Cookie");
165      Assert.assertTrue(header != null);
166      List<HttpCookie> cookies = HttpCookie.parse(header);
167      Assert.assertTrue(!cookies.isEmpty());
168      Assert.assertTrue(header.contains("; HttpOnly"));
169      Assert.assertTrue(cookies.get(0).getSecure());
170      Assert.assertTrue("token".equals(cookies.get(0).getValue()));
171    }
172  }
173
174  @AfterClass
175  public static void cleanup() throws Exception {
176    server.stop();
177    FileUtil.fullyDelete(new File(BASEDIR));
178    KeyStoreTestUtil.cleanupSSLConfig(keystoresDir, sslConfDir);
179    clientSslFactory.destroy();
180  }
181}