Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions solr/core/src/java/org/apache/solr/core/CoreContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ public CoreLoadFailure(CoreDescriptor cd, Exception loadFailure) {

private SecurityPluginHolder<AuthenticationPlugin> authenticationPlugin;

private boolean useShortUserName; // for authorization (See SOLR-10814)

private BackupRepositoryFactory backupRepoFactory;

protected SolrMetricManager metricManager;
Expand Down Expand Up @@ -682,6 +684,11 @@ public void securityNodeChanged() {
*/
private void reloadSecurityProperties() {
SecurityConfHandler.SecurityConfig securityConfig = securityConfHandler.getSecurityConfig(false);

String useShortUserNameStr = (String)securityConfig.getData().get("useShortUserName");
this.useShortUserName = (useShortUserNameStr != null) ?
Boolean.parseBoolean(useShortUserNameStr) : false; // Default is false.

initializeAuthorizationPlugin((Map<String, Object>) securityConfig.getData().get("authorization"));
initializeAuthenticationPlugin((Map<String, Object>) securityConfig.getData().get("authentication"));
}
Expand Down Expand Up @@ -1558,6 +1565,10 @@ public boolean isCoreLoading(String name) {
return solrCores.isCoreLoading(name);
}

public boolean useShortUserName() {
return useShortUserName;
}

public AuthorizationPlugin getAuthorizationPlugin() {
return authorizationPlugin == null ? null : authorizationPlugin.plugin;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,32 @@ public CollectionRequest(String collectionName) {
}

public abstract SolrParams getParams() ;


/**
* This method returns the {@linkplain Principal} corresponding to
* the authenticated user for the current request. Please note that
* the value returned by {@linkplain Principal#getName()} method depends
* upon the type of the authentication mechanism used (e.g. for user "foo"
* with BASIC authentication the result would be "foo". On the other hand
* with KERBEROS it would be foo@RELMNAME). Hence
* {@linkplain AuthorizationContext#getUserName()} method should be preferred
* to extract the identity of authenticated user instead of this method.
*
* @return user principal in case of an authenticated request
* null in case of unauthenticated request
*/
public abstract Principal getUserPrincipal() ;

/**
* This method returns the name of the authenticated user for the current request.
* The return value of this method is agnostic of the underlying authentication
* mechanism used.
*
* @return user name in case of an authenticated user
* null in case of unauthenticated request
*/
public abstract String getUserName() ;

public abstract String getHttpHeader(String header);

public abstract Enumeration getHeaderNames();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ public boolean doAuthenticate(ServletRequest servletRequest, ServletResponse ser
public Principal getUserPrincipal() {
return new BasicUserPrincipal(username);
}

@Override
public String getRemoteUser() {
return username;
}
};
filterChain.doFilter(wrapper, response);
return true;
Expand Down
9 changes: 9 additions & 0 deletions solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.BasicUserPrincipal;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
Expand Down Expand Up @@ -1033,9 +1034,17 @@ public SolrParams getParams() {

@Override
public Principal getUserPrincipal() {
if (cores.useShortUserName()) {
return new BasicUserPrincipal(getReq().getRemoteUser());
}
return getReq().getUserPrincipal();
}

@Override
public String getUserName() {
return getReq().getRemoteUser();
}

@Override
public String getHttpHeader(String s) {
return getReq().getHeader(s);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"useShortUserName": "true",
"authentication": {
"class": "org.apache.solr.security.ConfigurableInternodeAuthHadoopPlugin",
"sysPropPrefix": "solr.",
"type": "kerberos",
"clientBuilderFactory": "org.apache.solr.client.solrj.impl.Krb5HttpClientBuilder",
"initKerberosZk": "true",
"authConfigs": [
"kerberos.principal",
"kerberos.keytab",
"kerberos.name.rules"
],
"defaultConfigs": {
}
},
"authorization":{
"class":"solr.RuleBasedAuthorizationPlugin",
"permissions":[
{
"name":"collection-admin-edit",
"role":"admin"
},
{
"name":"read",
"role":"admin"
},
{
"name":"update",
"role":"admin"
}
],
"user-role":{
"solr":"admin"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,11 @@ public Principal getUserPrincipal() {
return userPrincipal == null ? null : new BasicUserPrincipal(String.valueOf(userPrincipal));
}

@Override
public String getUserName() {
return (getUserPrincipal() != null) ? getUserPrincipal().getName() : null;
}

@Override
public String getHttpHeader(String header) {
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* 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 java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;

import org.apache.commons.io.FileUtils;
import org.apache.solr.cloud.KerberosTestServices;

/**
* A utility class which provides common functionality required to test kerberos integration.
*/
public class KerberosUtils {

/**
* This method sets up Hadoop mini-kdc along with relevant Kerberos configuration files
* (e.g. jaas.conf) as well as system properties.
*
* @param baseDir The directory path which should be used by the Hadoop mini-kdc
* @return An instance of {@linkplain KerberosTestServices}
* @throws Exception in case of errors.
*/
static KerberosTestServices setupMiniKdc(Path baseDir) throws Exception {
System.setProperty("solr.jaas.debug", "true");
String kdcDir = baseDir+File.separator+"minikdc";
String solrClientPrincipal = "solr";
File keytabFile = new File(kdcDir, "keytabs");
KerberosTestServices tmp = KerberosTestServices.builder()
.withKdc(new File(kdcDir))
.withJaasConfiguration(solrClientPrincipal, keytabFile, "SolrClient")
.build();
String solrServerPrincipal = "HTTP/127.0.0.1";
tmp.start();
tmp.getKdc().createPrincipal(keytabFile, solrServerPrincipal, solrClientPrincipal);

String jaas = "SolrClient {\n"
+ " com.sun.security.auth.module.Krb5LoginModule required\n"
+ " useKeyTab=true\n"
+ " keyTab=\"" + keytabFile.getAbsolutePath() + "\"\n"
+ " storeKey=true\n"
+ " useTicketCache=false\n"
+ " doNotPrompt=true\n"
+ " debug=true\n"
+ " principal=\"" + solrClientPrincipal + "\";\n"
+ "};";

String jaasFilePath = kdcDir+File.separator+"jaas-client.conf";
FileUtils.write(new File(jaasFilePath), jaas, StandardCharsets.UTF_8);
System.setProperty("java.security.auth.login.config", jaasFilePath);
System.setProperty("solr.kerberos.jaas.appname", "SolrClient"); // Get this app name from the jaas file

System.setProperty("solr.kerberos.principal", solrServerPrincipal);
System.setProperty("solr.kerberos.keytab", keytabFile.getAbsolutePath());
// Extracts 127.0.0.1 from HTTP/127.0.0.1@EXAMPLE.COM
System.setProperty("solr.kerberos.name.rules", "RULE:[1:$1@$0](.*EXAMPLE.COM)s/@.*//"
+ "\nRULE:[2:$2@$0](.*EXAMPLE.COM)s/@.*//"
+ "\nDEFAULT"
);

return tmp;
}

/**
* This method stops the Hadoop mini-kdc instance as well as cleanup relevant Java system properties.
*
* @param kerberosTestServices An instance of Hadoop mini-kdc
*/
public static void cleanupMiniKdc (KerberosTestServices kerberosTestServices) {
System.clearProperty("java.security.auth.login.config");
System.clearProperty("solr.kerberos.principal");
System.clearProperty("solr.kerberos.keytab");
System.clearProperty("solr.kerberos.name.rules");
System.clearProperty("solr.jaas.debug");
if (kerberosTestServices != null) {
kerberosTestServices.stop();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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 org.apache.lucene.util.Constants;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.cloud.AbstractDistribZkTestBase;
import org.apache.solr.cloud.KerberosTestServices;
import org.apache.solr.cloud.SolrCloudTestCase;
import org.apache.solr.common.SolrInputDocument;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class TestRuleBasedAuthorizationWithKerberos extends SolrCloudTestCase {
protected static final int NUM_SERVERS = 1;
protected static final int NUM_SHARDS = 1;
protected static final int REPLICATION_FACTOR = 1;
private static KerberosTestServices kerberosTestServices;

@BeforeClass
public static void setupClass() throws Exception {
assumeFalse("Hadoop does not work on Windows", Constants.WINDOWS);
assumeFalse("FIXME: SOLR-8182: This test fails under Java 9", Constants.JRE_IS_MINIMUM_JAVA9);

kerberosTestServices = KerberosUtils.setupMiniKdc(createTempDir());

configureCluster(NUM_SERVERS)// nodes
.withSecurityJson(TEST_PATH().resolve("security").resolve("hadoop_kerberos_authz_config.json"))
.addConfig("conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
.configure();
}

@AfterClass
public static void tearDownClass() throws Exception {
KerberosUtils.cleanupMiniKdc(kerberosTestServices);
kerberosTestServices = null;
}

@Test
public void testCollectionCreateSearchDelete() throws Exception {
CloudSolrClient solrClient = cluster.getSolrClient();
String collectionName = "testkerberoscollection_authz";

// create collection
CollectionAdminRequest.Create create = CollectionAdminRequest.createCollection(collectionName, "conf1",
NUM_SHARDS, REPLICATION_FACTOR);
create.process(solrClient);

SolrInputDocument doc = new SolrInputDocument();
doc.setField("id", "1");
solrClient.add(collectionName, doc);
solrClient.commit(collectionName);

SolrQuery query = new SolrQuery();
query.setQuery("*:*");
QueryResponse rsp = solrClient.query(collectionName, query);
assertEquals(1, rsp.getResults().getNumFound());

CollectionAdminRequest.Delete deleteReq = CollectionAdminRequest.deleteCollection(collectionName);
deleteReq.process(solrClient);
AbstractDistribZkTestBase.waitForCollectionToDisappear(collectionName,
solrClient.getZkStateReader(), true, true, 330);
}

}
Loading