/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.solr.security.hadoop; import static org.apache.solr.security.HttpParamDelegationTokenPlugin.USER_PARAM; import static org.apache.solr.security.hadoop.ImpersonationUtil.*; import java.net.InetAddress; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; import org.apache.lucene.util.Constants; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.embedded.JettySolrRunner; import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.cloud.SolrCloudTestCase; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.util.Utils; import org.apache.solr.security.HadoopAuthPlugin; import org.apache.solr.servlet.SolrRequestParsers; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; public class TestImpersonationWithHadoopAuth extends SolrCloudTestCase { protected static final int NUM_SERVERS = 2; private static final boolean defaultAddRequestHeadersToContext = SolrRequestParsers.DEFAULT.isAddRequestHeadersToContext(); @SuppressWarnings("unchecked") @BeforeClass public static void setupClass() throws Exception { assumeFalse("Hadoop does not work on Windows", Constants.WINDOWS); InetAddress loopback = InetAddress.getLoopbackAddress(); Path securityJsonPath = TEST_PATH().resolve("security").resolve("hadoop_simple_auth_with_delegation.json"); String securityJson = new String(Files.readAllBytes(securityJsonPath), Charset.defaultCharset()); Map<String, Object> securityConfig = (Map<String, Object>)Utils.fromJSONString(securityJson); Map<String, Object> authConfig = (Map<String, Object>)securityConfig.get("authentication"); Map<String,String> proxyUserConfigs = (Map<String,String>) authConfig .getOrDefault(HadoopAuthPlugin.PROXY_USER_CONFIGS, new HashMap<>()); proxyUserConfigs.put("proxyuser.noGroups.hosts", "*"); proxyUserConfigs.put("proxyuser.anyHostAnyUser.hosts", "*"); proxyUserConfigs.put("proxyuser.anyHostAnyUser.groups", "*"); proxyUserConfigs.put("proxyuser.wrongHost.hosts", "1.1.1.1.1.1"); proxyUserConfigs.put("proxyuser.wrongHost.groups", "*"); proxyUserConfigs.put("proxyuser.noHosts.groups", "*"); proxyUserConfigs.put("proxyuser.localHostAnyGroup.hosts", loopback.getCanonicalHostName() + "," + loopback.getHostName() + "," + loopback.getHostAddress()); proxyUserConfigs.put("proxyuser.localHostAnyGroup.groups", "*"); proxyUserConfigs.put("proxyuser.bogusGroup.hosts", "*"); proxyUserConfigs.put("proxyuser.bogusGroup.groups", "__some_bogus_group"); proxyUserConfigs.put("proxyuser.anyHostUsersGroup.groups", ImpersonationUtil.getUsersFirstGroup()); proxyUserConfigs.put("proxyuser.anyHostUsersGroup.hosts", "*"); authConfig.put(HadoopAuthPlugin.PROXY_USER_CONFIGS, proxyUserConfigs); SolrRequestParsers.DEFAULT.setAddRequestHeadersToContext(true); System.setProperty("collectionsHandler", ImpersonatorCollectionsHandler.class.getName()); configureCluster(NUM_SERVERS)// nodes .withSecurityJson(Utils.toJSONString(securityConfig)) .addConfig("conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf")) .configure(); } @AfterClass public static void tearDownClass() throws Exception { SolrRequestParsers.DEFAULT.setAddRequestHeadersToContext(defaultAddRequestHeadersToContext); System.clearProperty("collectionsHandler"); } private SolrClient newSolrClient() { return new HttpSolrClient.Builder( cluster.getJettySolrRunner(0).getBaseUrl().toString()).build(); } @Test public void testProxyNoConfigGroups() throws Exception { try (SolrClient solrClient = newSolrClient()) { solrClient.request(getProxyRequest("noGroups","bar")); fail("Expected RemoteSolrException"); } catch (HttpSolrClient.RemoteSolrException ex) { assertTrue(ex.getLocalizedMessage(), ex.getMessage().contains(getExpectedGroupExMsg("noGroups", "bar"))); } } @Test public void testProxyWrongHost() throws Exception { try (SolrClient solrClient = newSolrClient()) { solrClient.request(getProxyRequest("wrongHost","bar")); fail("Expected RemoteSolrException"); } catch (HttpSolrClient.RemoteSolrException ex) { assertTrue(ex.getMessage().contains(getExpectedHostExMsg("wrongHost"))); } } @Test public void testProxyNoConfigHosts() throws Exception { try (SolrClient solrClient = newSolrClient()) { solrClient.request(getProxyRequest("noHosts","bar")); fail("Expected RemoteSolrException"); } catch (HttpSolrClient.RemoteSolrException ex) { assertTrue(ex.getMessage().contains(getExpectedHostExMsg("noHosts"))); } } @Test public void testProxyValidateAnyHostAnyUser() throws Exception { try (SolrClient solrClient = newSolrClient()) { solrClient.request(getProxyRequest("anyHostAnyUser", "bar")); assertTrue(ImpersonatorCollectionsHandler.called.get()); } } @Test public void testProxyInvalidProxyUser() throws Exception { try (SolrClient solrClient = newSolrClient()) { // wrong direction, should fail solrClient.request(getProxyRequest("bar","anyHostAnyUser")); fail("Expected RemoteSolrException"); } catch (HttpSolrClient.RemoteSolrException ex) { assertTrue(ex.getMessage().contains(getExpectedGroupExMsg("bar", "anyHostAnyUser"))); } } @Test public void testProxyValidateHost() throws Exception { try (SolrClient solrClient = newSolrClient()) { solrClient.request(getProxyRequest("localHostAnyGroup", "bar")); assertTrue(ImpersonatorCollectionsHandler.called.get()); } } @Test public void testProxyValidateGroup() throws Exception { try (SolrClient solrClient = newSolrClient()) { solrClient.request(getProxyRequest("anyHostUsersGroup", System.getProperty("user.name"))); assertTrue(ImpersonatorCollectionsHandler.called.get()); } } @Test public void testProxyInvalidGroup() throws Exception { try (SolrClient solrClient = newSolrClient()) { solrClient.request(getProxyRequest("bogusGroup","bar")); fail("Expected RemoteSolrException"); } catch (HttpSolrClient.RemoteSolrException ex) { assertTrue(ex.getMessage().contains(getExpectedGroupExMsg("bogusGroup", "bar"))); } } @Test public void testProxyNullProxyUser() throws Exception { try (SolrClient solrClient = newSolrClient()) { solrClient.request(getProxyRequest("","bar")); fail("Expected RemoteSolrException"); } catch (HttpSolrClient.RemoteSolrException ex) { // this exception is specific to our implementation, don't check a specific message. } } @Test @AwaitsFix(bugUrl="https://issues.apache.org/jira/browse/HADOOP-9893") public void testForwarding() throws Exception { String collectionName = "forwardingCollection"; // create collection CollectionAdminRequest.Create create = CollectionAdminRequest.createCollection(collectionName, "conf1", 1, 1); try (SolrClient solrClient = newSolrClient()) { create.process(solrClient); } // try a command to each node, one of them must be forwarded for (JettySolrRunner jetty : cluster.getJettySolrRunners()) { HttpSolrClient client = new HttpSolrClient.Builder(jetty.getBaseUrl().toString() + "/" + collectionName).build(); try { ModifiableSolrParams params = new ModifiableSolrParams(); params.set("q", "*:*"); params.set(USER_PARAM, "user"); client.query(params); } finally { client.close(); } } } }