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.rest; 019 020import static org.apache.hadoop.hbase.rest.RESTServlet.HBASE_REST_SUPPORT_PROXYUSER; 021import static org.junit.jupiter.api.Assertions.assertEquals; 022import static org.junit.jupiter.api.Assertions.assertTrue; 023 024import com.fasterxml.jackson.databind.ObjectMapper; 025import java.io.File; 026import java.io.IOException; 027import java.net.HttpURLConnection; 028import java.net.URL; 029import java.security.Principal; 030import java.security.PrivilegedExceptionAction; 031import org.apache.commons.io.FileUtils; 032import org.apache.hadoop.conf.Configuration; 033import org.apache.hadoop.hbase.HBaseTestingUtil; 034import org.apache.hadoop.hbase.SingleProcessHBaseCluster; 035import org.apache.hadoop.hbase.StartTestingClusterOption; 036import org.apache.hadoop.hbase.TableName; 037import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 038import org.apache.hadoop.hbase.client.Connection; 039import org.apache.hadoop.hbase.client.ConnectionFactory; 040import org.apache.hadoop.hbase.client.Put; 041import org.apache.hadoop.hbase.client.Table; 042import org.apache.hadoop.hbase.client.TableDescriptor; 043import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 044import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 045import org.apache.hadoop.hbase.http.ssl.KeyStoreTestUtil; 046import org.apache.hadoop.hbase.rest.model.CellModel; 047import org.apache.hadoop.hbase.rest.model.CellSetModel; 048import org.apache.hadoop.hbase.rest.model.RowModel; 049import org.apache.hadoop.hbase.rest.model.ScannerModel; 050import org.apache.hadoop.hbase.security.HBaseKerberosUtils; 051import org.apache.hadoop.hbase.security.access.AccessControlClient; 052import org.apache.hadoop.hbase.security.access.AccessControlConstants; 053import org.apache.hadoop.hbase.security.access.AccessController; 054import org.apache.hadoop.hbase.security.access.Permission.Action; 055import org.apache.hadoop.hbase.security.token.TokenProvider; 056import org.apache.hadoop.hbase.testclassification.MediumTests; 057import org.apache.hadoop.hbase.testclassification.MiscTests; 058import org.apache.hadoop.hbase.util.Bytes; 059import org.apache.hadoop.hbase.util.Pair; 060import org.apache.hadoop.hdfs.DFSConfigKeys; 061import org.apache.hadoop.http.HttpConfig; 062import org.apache.hadoop.minikdc.MiniKdc; 063import org.apache.hadoop.security.UserGroupInformation; 064import org.apache.hadoop.security.authentication.util.KerberosName; 065import org.apache.http.HttpEntity; 066import org.apache.http.HttpHost; 067import org.apache.http.auth.AuthSchemeProvider; 068import org.apache.http.auth.AuthScope; 069import org.apache.http.auth.Credentials; 070import org.apache.http.client.AuthCache; 071import org.apache.http.client.CredentialsProvider; 072import org.apache.http.client.config.AuthSchemes; 073import org.apache.http.client.methods.CloseableHttpResponse; 074import org.apache.http.client.methods.HttpDelete; 075import org.apache.http.client.methods.HttpGet; 076import org.apache.http.client.methods.HttpPost; 077import org.apache.http.client.methods.HttpPut; 078import org.apache.http.client.protocol.HttpClientContext; 079import org.apache.http.config.Registry; 080import org.apache.http.config.RegistryBuilder; 081import org.apache.http.conn.HttpClientConnectionManager; 082import org.apache.http.entity.ContentType; 083import org.apache.http.entity.StringEntity; 084import org.apache.http.impl.auth.SPNegoSchemeFactory; 085import org.apache.http.impl.client.BasicAuthCache; 086import org.apache.http.impl.client.BasicCredentialsProvider; 087import org.apache.http.impl.client.CloseableHttpClient; 088import org.apache.http.impl.client.HttpClients; 089import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; 090import org.apache.http.util.EntityUtils; 091import org.junit.jupiter.api.AfterAll; 092import org.junit.jupiter.api.BeforeAll; 093import org.junit.jupiter.api.Tag; 094import org.junit.jupiter.api.Test; 095import org.slf4j.Logger; 096import org.slf4j.LoggerFactory; 097 098import org.apache.hbase.thirdparty.com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; 099import org.apache.hbase.thirdparty.javax.ws.rs.core.MediaType; 100 101/** 102 * Test class for SPNEGO authentication on the HttpServer. Uses Kerby's MiniKDC and Apache 103 * HttpComponents to verify that a simple Servlet is reachable via SPNEGO and unreachable w/o. 104 */ 105@Tag(MiscTests.TAG) 106@Tag(MediumTests.TAG) 107public class TestSecureRESTServer { 108 109 private static final Logger LOG = LoggerFactory.getLogger(TestSecureRESTServer.class); 110 private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 111 private static final HBaseRESTTestingUtility REST_TEST = new HBaseRESTTestingUtility(); 112 private static SingleProcessHBaseCluster CLUSTER; 113 114 private static final String HOSTNAME = "localhost"; 115 private static final String CLIENT_PRINCIPAL = "client"; 116 private static final String CLIENT_PRINCIPAL2 = "client2"; 117 private static final String WHEEL_PRINCIPAL = "wheel"; 118 // The principal for accepting SPNEGO authn'ed requests (*must* be HTTP/fqdn) 119 private static final String SPNEGO_SERVICE_PRINCIPAL = "HTTP/" + HOSTNAME; 120 // The principal we use to connect to HBase 121 private static final String REST_SERVER_PRINCIPAL = "rest"; 122 private static final String SERVICE_PRINCIPAL = "hbase/" + HOSTNAME; 123 124 private static URL baseUrl; 125 private static MiniKdc KDC; 126 private static RESTServer server; 127 private static File restServerKeytab; 128 private static File clientKeytab; 129 private static File wheelKeytab; 130 private static File serviceKeytab; 131 132 @BeforeAll 133 public static void setupServer() throws Exception { 134 final File target = new File(System.getProperty("user.dir"), "target"); 135 assertTrue(target.exists()); 136 137 /* 138 * Keytabs 139 */ 140 File keytabDir = new File(target, TestSecureRESTServer.class.getSimpleName() + "_keytabs"); 141 if (keytabDir.exists()) { 142 FileUtils.deleteDirectory(keytabDir); 143 } 144 keytabDir.mkdirs(); 145 // Keytab for HBase services (RS, Master) 146 serviceKeytab = new File(keytabDir, "hbase.service.keytab"); 147 // The keytab for the REST server 148 restServerKeytab = new File(keytabDir, "spnego.keytab"); 149 // Keytab for the client 150 clientKeytab = new File(keytabDir, CLIENT_PRINCIPAL + ".keytab"); 151 // Keytab for wheel 152 wheelKeytab = new File(keytabDir, WHEEL_PRINCIPAL + ".keytab"); 153 154 /* 155 * Update UGI 156 */ 157 Configuration conf = TEST_UTIL.getConfiguration(); 158 159 /* 160 * Start KDC 161 */ 162 KDC = TEST_UTIL.setupMiniKdc(serviceKeytab); 163 KDC.createPrincipal(clientKeytab, CLIENT_PRINCIPAL, CLIENT_PRINCIPAL2); 164 KDC.createPrincipal(wheelKeytab, WHEEL_PRINCIPAL); 165 KDC.createPrincipal(serviceKeytab, SERVICE_PRINCIPAL); 166 // REST server's keytab contains keys for both principals REST uses 167 KDC.createPrincipal(restServerKeytab, SPNEGO_SERVICE_PRINCIPAL, REST_SERVER_PRINCIPAL); 168 169 // Set configuration for HBase 170 HBaseKerberosUtils.setPrincipalForTesting(SERVICE_PRINCIPAL + "@" + KDC.getRealm()); 171 HBaseKerberosUtils.setKeytabFileForTesting(serviceKeytab.getAbsolutePath()); 172 // Why doesn't `setKeytabFileForTesting` do this? 173 conf.set("hbase.master.keytab.file", serviceKeytab.getAbsolutePath()); 174 conf.set("hbase.unsafe.regionserver.hostname", "localhost"); 175 conf.set("hbase.master.hostname", "localhost"); 176 HBaseKerberosUtils.setSecuredConfiguration(conf, SERVICE_PRINCIPAL + "@" + KDC.getRealm(), 177 SPNEGO_SERVICE_PRINCIPAL + "@" + KDC.getRealm()); 178 setHdfsSecuredConfiguration(conf); 179 conf.setStrings(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, TokenProvider.class.getName(), 180 AccessController.class.getName()); 181 conf.setStrings(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, AccessController.class.getName()); 182 conf.setStrings(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY, 183 AccessController.class.getName()); 184 // Enable EXEC permission checking 185 conf.setBoolean(AccessControlConstants.EXEC_PERMISSION_CHECKS_KEY, true); 186 conf.set("hbase.superuser", "hbase"); 187 conf.set("hadoop.proxyuser.rest.hosts", "*"); 188 conf.set("hadoop.proxyuser.rest.users", "*"); 189 conf.set("hadoop.proxyuser.wheel.hosts", "*"); 190 conf.set("hadoop.proxyuser.wheel.users", "*"); 191 UserGroupInformation.setConfiguration(conf); 192 193 updateKerberosConfiguration(conf, REST_SERVER_PRINCIPAL, SPNEGO_SERVICE_PRINCIPAL, 194 restServerKeytab); 195 196 // Start HBase 197 TEST_UTIL.startMiniCluster(StartTestingClusterOption.builder().numMasters(1).numRegionServers(1) 198 .numZkServers(1).build()); 199 200 // Start REST 201 UserGroupInformation restUser = UserGroupInformation 202 .loginUserFromKeytabAndReturnUGI(REST_SERVER_PRINCIPAL, restServerKeytab.getAbsolutePath()); 203 restUser.doAs(new PrivilegedExceptionAction<Void>() { 204 @Override 205 public Void run() throws Exception { 206 REST_TEST.startServletContainer(conf); 207 return null; 208 } 209 }); 210 baseUrl = new URL("http://localhost:" + REST_TEST.getServletPort()); 211 212 LOG.info("HTTP server started: " + baseUrl); 213 TEST_UTIL.waitTableAvailable(TableName.valueOf("hbase:acl")); 214 215 // Let the REST server create, read, and write globally 216 UserGroupInformation superuser = UserGroupInformation 217 .loginUserFromKeytabAndReturnUGI(SERVICE_PRINCIPAL, serviceKeytab.getAbsolutePath()); 218 superuser.doAs(new PrivilegedExceptionAction<Void>() { 219 @Override 220 public Void run() throws Exception { 221 try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) { 222 AccessControlClient.grant(conn, REST_SERVER_PRINCIPAL, Action.CREATE, Action.READ, 223 Action.WRITE); 224 } catch (Throwable t) { 225 if (t instanceof Exception) { 226 throw (Exception) t; 227 } else { 228 throw new Exception(t); 229 } 230 } 231 return null; 232 } 233 }); 234 instertData(); 235 } 236 237 @AfterAll 238 public static void stopServer() throws Exception { 239 try { 240 if (null != server) { 241 server.stop(); 242 } 243 } catch (Exception e) { 244 LOG.info("Failed to stop info server", e); 245 } 246 try { 247 if (CLUSTER != null) { 248 CLUSTER.shutdown(); 249 } 250 } catch (Exception e) { 251 LOG.info("Failed to stop HBase cluster", e); 252 } 253 try { 254 if (null != KDC) { 255 KDC.stop(); 256 } 257 } catch (Exception e) { 258 LOG.info("Failed to stop mini KDC", e); 259 } 260 } 261 262 private static void setHdfsSecuredConfiguration(Configuration conf) throws Exception { 263 // Set principal+keytab configuration for HDFS 264 conf.set(DFSConfigKeys.DFS_NAMENODE_KERBEROS_PRINCIPAL_KEY, 265 SERVICE_PRINCIPAL + "@" + KDC.getRealm()); 266 conf.set(DFSConfigKeys.DFS_NAMENODE_KEYTAB_FILE_KEY, serviceKeytab.getAbsolutePath()); 267 conf.set(DFSConfigKeys.DFS_DATANODE_KERBEROS_PRINCIPAL_KEY, 268 SERVICE_PRINCIPAL + "@" + KDC.getRealm()); 269 conf.set(DFSConfigKeys.DFS_DATANODE_KEYTAB_FILE_KEY, serviceKeytab.getAbsolutePath()); 270 conf.set(DFSConfigKeys.DFS_WEB_AUTHENTICATION_KERBEROS_PRINCIPAL_KEY, 271 SPNEGO_SERVICE_PRINCIPAL + "@" + KDC.getRealm()); 272 // Enable token access for HDFS blocks 273 conf.setBoolean(DFSConfigKeys.DFS_BLOCK_ACCESS_TOKEN_ENABLE_KEY, true); 274 // Only use HTTPS (required because we aren't using "secure" ports) 275 conf.set(DFSConfigKeys.DFS_HTTP_POLICY_KEY, HttpConfig.Policy.HTTPS_ONLY.name()); 276 // Bind on localhost for spnego to have a chance at working 277 conf.set(DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_KEY, "localhost:0"); 278 conf.set(DFSConfigKeys.DFS_DATANODE_HTTPS_ADDRESS_KEY, "localhost:0"); 279 280 // Generate SSL certs 281 File keystoresDir = new File(TEST_UTIL.getDataTestDir("keystore").toUri().getPath()); 282 keystoresDir.mkdirs(); 283 String sslConfDir = KeyStoreTestUtil.getClasspathDir(TestSecureRESTServer.class); 284 KeyStoreTestUtil.setupSSLConfig(keystoresDir.getAbsolutePath(), sslConfDir, conf, false); 285 286 // Magic flag to tell hdfs to not fail on using ports above 1024 287 conf.setBoolean("ignore.secure.ports.for.testing", true); 288 } 289 290 private static void updateKerberosConfiguration(Configuration conf, String serverPrincipal, 291 String spnegoPrincipal, File serverKeytab) { 292 KerberosName.setRules("DEFAULT"); 293 294 // Enable Kerberos (pre-req) 295 conf.set("hbase.security.authentication", "kerberos"); 296 conf.set(RESTServer.REST_AUTHENTICATION_TYPE, "kerberos"); 297 // User to talk to HBase as 298 conf.set(RESTServer.REST_KERBEROS_PRINCIPAL, serverPrincipal); 299 // User to accept SPNEGO-auth'd http calls as 300 conf.set("hbase.rest.authentication.kerberos.principal", spnegoPrincipal); 301 // Keytab for both principals above 302 conf.set(RESTServer.REST_KEYTAB_FILE, serverKeytab.getAbsolutePath()); 303 conf.set("hbase.rest.authentication.kerberos.keytab", serverKeytab.getAbsolutePath()); 304 conf.set(HBASE_REST_SUPPORT_PROXYUSER, "true"); 305 } 306 307 private static void instertData() throws IOException, InterruptedException { 308 // Create a table, write a row to it, grant read perms to the client 309 UserGroupInformation superuser = UserGroupInformation 310 .loginUserFromKeytabAndReturnUGI(SERVICE_PRINCIPAL, serviceKeytab.getAbsolutePath()); 311 final TableName table = TableName.valueOf("publicTable"); 312 superuser.doAs(new PrivilegedExceptionAction<Void>() { 313 @Override 314 public Void run() throws Exception { 315 try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) { 316 TableDescriptor desc = TableDescriptorBuilder.newBuilder(table) 317 .setColumnFamily(ColumnFamilyDescriptorBuilder.of("f1")).build(); 318 conn.getAdmin().createTable(desc); 319 try (Table t = conn.getTable(table)) { 320 Put p = new Put(Bytes.toBytes("a")); 321 p.addColumn(Bytes.toBytes("f1"), new byte[0], Bytes.toBytes("1")); 322 t.put(p); 323 } 324 AccessControlClient.grant(conn, CLIENT_PRINCIPAL, Action.READ); 325 } catch (Throwable e) { 326 if (e instanceof Exception) { 327 throw (Exception) e; 328 } else { 329 throw new Exception(e); 330 } 331 } 332 return null; 333 } 334 }); 335 } 336 337 private void testProxy(String extraArgs, String PRINCIPAL, File keytab, int responseCode) 338 throws Exception { 339 UserGroupInformation.loginUserFromKeytabAndReturnUGI(SERVICE_PRINCIPAL, 340 serviceKeytab.getAbsolutePath()); 341 final TableName table = TableName.valueOf("publicTable"); 342 343 // Read that row as the client 344 Pair<CloseableHttpClient, HttpClientContext> pair = getClient(); 345 CloseableHttpClient client = pair.getFirst(); 346 HttpClientContext context = pair.getSecond(); 347 348 HttpGet get = new HttpGet(new URL("http://localhost:" + REST_TEST.getServletPort()).toURI() 349 + "/" + table + "/a" + extraArgs); 350 get.addHeader("Accept", "application/json"); 351 UserGroupInformation user = 352 UserGroupInformation.loginUserFromKeytabAndReturnUGI(PRINCIPAL, keytab.getAbsolutePath()); 353 String jsonResponse = user.doAs(new PrivilegedExceptionAction<String>() { 354 @Override 355 public String run() throws Exception { 356 try (CloseableHttpResponse response = client.execute(get, context)) { 357 final int statusCode = response.getStatusLine().getStatusCode(); 358 assertEquals(responseCode, statusCode, response.getStatusLine().toString()); 359 HttpEntity entity = response.getEntity(); 360 return EntityUtils.toString(entity); 361 } 362 } 363 }); 364 if (responseCode == HttpURLConnection.HTTP_OK) { 365 ObjectMapper mapper = new JacksonJaxbJsonProvider().locateMapper(CellSetModel.class, 366 MediaType.APPLICATION_JSON_TYPE); 367 CellSetModel model = mapper.readValue(jsonResponse, CellSetModel.class); 368 assertEquals(1, model.getRows().size()); 369 RowModel row = model.getRows().get(0); 370 assertEquals("a", Bytes.toString(row.getKey())); 371 assertEquals(1, row.getCells().size()); 372 CellModel cell = row.getCells().get(0); 373 assertEquals("1", Bytes.toString(cell.getValue())); 374 } 375 } 376 377 @Test 378 public void testPositiveAuthorization() throws Exception { 379 testProxy("", CLIENT_PRINCIPAL, clientKeytab, HttpURLConnection.HTTP_OK); 380 } 381 382 @Test 383 public void testDoAs() throws Exception { 384 testProxy("?doAs=" + CLIENT_PRINCIPAL, WHEEL_PRINCIPAL, wheelKeytab, HttpURLConnection.HTTP_OK); 385 } 386 387 @Test 388 public void testDoas() throws Exception { 389 testProxy("?doas=" + CLIENT_PRINCIPAL, WHEEL_PRINCIPAL, wheelKeytab, HttpURLConnection.HTTP_OK); 390 } 391 392 @Test 393 public void testWithoutDoAs() throws Exception { 394 testProxy("", WHEEL_PRINCIPAL, wheelKeytab, HttpURLConnection.HTTP_FORBIDDEN); 395 } 396 397 @Test 398 public void testNegativeAuthorization() throws Exception { 399 Pair<CloseableHttpClient, HttpClientContext> pair = getClient(); 400 CloseableHttpClient client = pair.getFirst(); 401 HttpClientContext context = pair.getSecond(); 402 403 StringEntity entity = new StringEntity( 404 "{\"name\":\"test\", \"ColumnSchema\":[{\"name\":\"f\"}]}", ContentType.APPLICATION_JSON); 405 HttpPut put = new HttpPut("http://localhost:" + REST_TEST.getServletPort() + "/test/schema"); 406 put.setEntity(entity); 407 408 UserGroupInformation unprivileged = UserGroupInformation 409 .loginUserFromKeytabAndReturnUGI(CLIENT_PRINCIPAL, clientKeytab.getAbsolutePath()); 410 unprivileged.doAs(new PrivilegedExceptionAction<Void>() { 411 @Override 412 public Void run() throws Exception { 413 try (CloseableHttpResponse response = client.execute(put, context)) { 414 final int statusCode = response.getStatusLine().getStatusCode(); 415 HttpEntity entity = response.getEntity(); 416 assertEquals(HttpURLConnection.HTTP_FORBIDDEN, statusCode, 417 "Got response: " + EntityUtils.toString(entity)); 418 } 419 return null; 420 } 421 }); 422 } 423 424 @Test 425 public void testScanWithDifferentClients() throws Exception { 426 Pair<CloseableHttpClient, HttpClientContext> pair = getClient(); 427 CloseableHttpClient client = pair.getFirst(); 428 HttpClientContext context = pair.getSecond(); 429 430 UserGroupInformation ugi = UserGroupInformation 431 .loginUserFromKeytabAndReturnUGI(CLIENT_PRINCIPAL, clientKeytab.getAbsolutePath()); 432 433 ObjectMapper mapper = new JacksonJaxbJsonProvider().locateMapper(ScannerModel.class, 434 MediaType.APPLICATION_JSON_TYPE); 435 TableName table = TableName.valueOf("publicTable"); 436 ScannerModel model = new ScannerModel(); 437 StringEntity entity = 438 new StringEntity(mapper.writeValueAsString(model), ContentType.APPLICATION_JSON); 439 HttpPost post = 440 new HttpPost("http://localhost:" + REST_TEST.getServletPort() + "/" + table + "/scanner"); 441 post.setEntity(entity); 442 String scannerURI = ugi.doAs(new PrivilegedExceptionAction<String>() { 443 444 @Override 445 public String run() throws Exception { 446 try (CloseableHttpResponse response = client.execute(post, context)) { 447 final int statusCode = response.getStatusLine().getStatusCode(); 448 assertEquals(HttpURLConnection.HTTP_CREATED, statusCode); 449 return response.getFirstHeader("Location").getValue(); 450 } 451 } 452 }); 453 454 Pair<CloseableHttpClient, HttpClientContext> pair2 = getClient(); 455 CloseableHttpClient client2 = pair2.getFirst(); 456 HttpClientContext context2 = pair2.getSecond(); 457 458 UserGroupInformation ugi2 = UserGroupInformation 459 .loginUserFromKeytabAndReturnUGI(CLIENT_PRINCIPAL2, clientKeytab.getAbsolutePath()); 460 ugi2.doAs(new PrivilegedExceptionAction<Void>() { 461 462 @Override 463 public Void run() throws Exception { 464 HttpGet get = new HttpGet(scannerURI + "?n=1"); 465 try (CloseableHttpResponse response = client2.execute(get, context2)) { 466 final int statusCode = response.getStatusLine().getStatusCode(); 467 assertEquals(HttpURLConnection.HTTP_FORBIDDEN, statusCode); 468 } 469 HttpDelete delete = new HttpDelete(scannerURI); 470 try (CloseableHttpResponse response = client2.execute(delete, context2)) { 471 final int statusCode = response.getStatusLine().getStatusCode(); 472 assertEquals(HttpURLConnection.HTTP_FORBIDDEN, statusCode); 473 } 474 return null; 475 } 476 }); 477 478 ugi.doAs(new PrivilegedExceptionAction<Void>() { 479 480 @Override 481 public Void run() throws Exception { 482 HttpGet get = new HttpGet(scannerURI + "?n=1"); 483 try (CloseableHttpResponse response = client.execute(get, context)) { 484 final int statusCode = response.getStatusLine().getStatusCode(); 485 assertEquals(HttpURLConnection.HTTP_OK, statusCode); 486 } 487 HttpDelete delete = new HttpDelete(scannerURI); 488 try (CloseableHttpResponse response = client.execute(delete, context)) { 489 final int statusCode = response.getStatusLine().getStatusCode(); 490 assertEquals(HttpURLConnection.HTTP_OK, statusCode); 491 } 492 return null; 493 } 494 }); 495 496 } 497 498 private Pair<CloseableHttpClient, HttpClientContext> getClient() { 499 HttpClientConnectionManager pool = new PoolingHttpClientConnectionManager(); 500 HttpHost host = new HttpHost("localhost", REST_TEST.getServletPort()); 501 Registry<AuthSchemeProvider> authRegistry = RegistryBuilder.<AuthSchemeProvider> create() 502 .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(true, true)).build(); 503 CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); 504 credentialsProvider.setCredentials(AuthScope.ANY, EmptyCredentials.INSTANCE); 505 AuthCache authCache = new BasicAuthCache(); 506 507 CloseableHttpClient client = HttpClients.custom().setDefaultAuthSchemeRegistry(authRegistry) 508 .setConnectionManager(pool).build(); 509 510 HttpClientContext context = HttpClientContext.create(); 511 context.setTargetHost(host); 512 context.setCredentialsProvider(credentialsProvider); 513 context.setAuthSchemeRegistry(authRegistry); 514 context.setAuthCache(authCache); 515 516 return new Pair<>(client, context); 517 } 518 519 private static class EmptyCredentials implements Credentials { 520 public static final EmptyCredentials INSTANCE = new EmptyCredentials(); 521 522 @Override 523 public String getPassword() { 524 return null; 525 } 526 527 @Override 528 public Principal getUserPrincipal() { 529 return null; 530 } 531 } 532}