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.IOException; 021import java.net.HttpURLConnection; 022import java.net.URL; 023import org.apache.commons.codec.binary.Base64; 024import org.apache.directory.ldap.client.template.LdapConnectionTemplate; 025import org.apache.directory.server.core.api.DirectoryService; 026import org.apache.directory.server.core.integ.ApacheDSTestExtension; 027import org.apache.directory.server.ldap.LdapServer; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.hbase.http.resource.JerseyResource; 030import org.junit.jupiter.api.AfterAll; 031import org.junit.jupiter.api.BeforeAll; 032import org.junit.jupiter.api.extension.ExtendWith; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036/** 037 * Base class for setting up and testing an HTTP server with LDAP authentication. 038 */ 039@ExtendWith(ApacheDSTestExtension.class) 040public class LdapServerTestBase extends HttpServerFunctionalTest { 041 private static final Logger LOG = LoggerFactory.getLogger(LdapServerTestBase.class); 042 043 protected static HttpServer server; 044 protected static URL baseUrl; 045 046 /** 047 * The following fields are set by ApacheDSTestExtension. These are normally inherited from 048 * AbstractLdapTestUnit, but this class already has a parent. We only use ldapServer, but 049 * declaring that one alone does not work. 050 */ 051 052 /** The class DirectoryService instance */ 053 public static DirectoryService classDirectoryService; 054 055 /** The test DirectoryService instance */ 056 public static DirectoryService methodDirectoryService; 057 058 /** The current DirectoryService instance */ 059 public static DirectoryService directoryService; 060 061 /** The class LdapServer instance */ 062 public static LdapServer classLdapServer; 063 064 /** The test LdapServer instance */ 065 public static LdapServer methodLdapServer; 066 067 /** The current LdapServer instance */ 068 public static LdapServer ldapServer; 069 070 /** The Ldap connection template */ 071 public static LdapConnectionTemplate ldapConnectionTemplate; 072 073 /** The current revision */ 074 public static long revision = 0L; 075 076 /** 077 * End of fields required by ApacheDSTestExtension 078 */ 079 080 private static final String AUTH_TYPE = "Basic "; 081 082 protected static LdapServer getLdapServer() { 083 return classLdapServer; 084 } 085 086 /** 087 * Sets up the HTTP server with LDAP authentication before any tests are run. 088 * @throws Exception if an error occurs during server setup 089 */ 090 @BeforeAll 091 public static void setupServer() throws Exception { 092 Configuration conf = new Configuration(); 093 setLdapConfigurations(conf); 094 095 server = createTestServer(conf); 096 server.addUnprivilegedServlet("echo", "/echo", TestHttpServer.EchoServlet.class); 097 server.addJerseyResourcePackage(JerseyResource.class.getPackage().getName(), "/jersey/*"); 098 server.start(); 099 100 baseUrl = getServerURL(server); 101 LOG.info("HTTP server started: " + baseUrl); 102 } 103 104 /** 105 * Stops the HTTP server after all tests are completed. 106 * @throws Exception if an error occurs during server shutdown 107 */ 108 @AfterAll 109 public static void stopServer() throws Exception { 110 try { 111 if (null != server) { 112 server.stop(); 113 } 114 } catch (Exception e) { 115 LOG.info("Failed to stop info server", e); 116 } 117 } 118 119 /** 120 * Configures the provided Configuration object for LDAP authentication. 121 * @param conf the Configuration object to set LDAP properties on 122 * @return the configured Configuration object 123 */ 124 protected static void setLdapConfigurations(Configuration conf) { 125 conf.setInt(HttpServer.HTTP_MAX_THREADS, TestHttpServer.MAX_THREADS); 126 127 // Enable LDAP (pre-req) 128 conf.set(HttpServer.HTTP_UI_AUTHENTICATION, "ldap"); 129 conf.set(HttpServer.FILTER_INITIALIZERS_PROPERTY, 130 "org.apache.hadoop.hbase.http.lib.AuthenticationFilterInitializer"); 131 conf.set("hadoop.http.authentication.type", "ldap"); 132 conf.set("hadoop.http.authentication.ldap.providerurl", 133 String.format("ldap://%s:%s", LdapConstants.LDAP_SERVER_ADDR, getLdapServer().getPort())); 134 conf.set("hadoop.http.authentication.ldap.enablestarttls", "false"); 135 conf.set("hadoop.http.authentication.ldap.basedn", LdapConstants.LDAP_BASE_DN); 136 } 137 138 /** 139 * Generates a Basic Authentication header from the provided credentials. 140 * @param credentials the credentials to encode 141 * @return the Basic Authentication header 142 */ 143 private String getBasicAuthHeader(String credentials) { 144 return AUTH_TYPE + new Base64(0).encodeToString(credentials.getBytes()); 145 } 146 147 /** 148 * Opens an HTTP connection to the specified endpoint with optional Basic Authentication. 149 * @param endpoint the endpoint to connect to 150 * @param credentials the credentials for Basic Authentication (optional) 151 * @return the opened HttpURLConnection 152 * @throws IOException if an error occurs while opening the connection 153 */ 154 protected HttpURLConnection openConnection(String endpoint, String credentials) 155 throws IOException { 156 URL url = new URL(getServerURL(server) + endpoint); 157 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 158 if (credentials != null) { 159 conn.setRequestProperty("Authorization", getBasicAuthHeader(credentials)); 160 } 161 return conn; 162 } 163}