From 6c04d63e633f74b4df9f77ad78b2335d9dd87470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Thu, 13 Oct 2016 10:46:30 +0200 Subject: [PATCH 01/26] SOLR-9481: Authentication and Authorization plugins now work in standalone mode, including edit API --- solr/CHANGES.txt | 3 + .../org/apache/solr/core/CoreContainer.java | 31 +-- .../handler/admin/SecurityConfHandler.java | 138 +++++++--- .../admin/SecurityConfHandlerLocal.java | 101 ++++++++ .../handler/admin/SecurityConfHandlerZk.java | 92 +++++++ .../apache/solr/security/BasicAuthPlugin.java | 14 +- .../SecurityConfHandlerLocalForTest.java | 39 +++ .../admin/SecurityConfHandlerTest.java | 66 ++--- .../security/BasicAuthIntegrationTest.java | 4 +- .../security/BasicAuthStandaloneTest.java | 238 ++++++++++++++++++ 10 files changed, 633 insertions(+), 93 deletions(-) create mode 100644 solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerLocal.java create mode 100644 solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerZk.java create mode 100644 solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerLocalForTest.java create mode 100644 solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 7124d403e17f..c65c22fe546b 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -127,6 +127,9 @@ New Features * SOLR-9103: Restore ability for users to add custom Streaming Expressions (Cao Manh Dat) +* SOLR-9481: Authentication and Authorization plugins now work in standalone mode if security.json is placed in + SOLR_HOME on every node. Editing config through API is supported but affects only that one node. (janhoy) + Bug Fixes ---------------------- diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java index fa8a8c0945ef..357bfd0dfd5a 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -48,7 +48,6 @@ import org.apache.solr.cloud.ZkController; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; -import org.apache.solr.common.cloud.ZkStateReader; import org.apache.solr.common.util.ExecutorUtil; import org.apache.solr.common.util.IOUtils; import org.apache.solr.common.util.Utils; @@ -60,6 +59,8 @@ import org.apache.solr.handler.admin.CoreAdminHandler; import org.apache.solr.handler.admin.InfoHandler; import org.apache.solr.handler.admin.SecurityConfHandler; +import org.apache.solr.handler.admin.SecurityConfHandlerLocal; +import org.apache.solr.handler.admin.SecurityConfHandlerZk; import org.apache.solr.handler.admin.ZookeeperInfoHandler; import org.apache.solr.handler.component.ShardHandlerFactory; import org.apache.solr.logging.LogWatcher; @@ -78,7 +79,6 @@ import org.slf4j.LoggerFactory; import static com.google.common.base.Preconditions.checkNotNull; -import static java.util.Collections.EMPTY_MAP; import static org.apache.solr.common.params.CommonParams.AUTHC_PATH; import static org.apache.solr.common.params.CommonParams.AUTHZ_PATH; import static org.apache.solr.common.params.CommonParams.COLLECTIONS_HANDLER_PATH; @@ -88,7 +88,6 @@ import static org.apache.solr.common.params.CommonParams.ZK_PATH; import static org.apache.solr.security.AuthenticationPlugin.AUTHENTICATION_PLUGIN_PROP; - /** * * @since solr 1.3 @@ -296,10 +295,10 @@ private synchronized void initializeAuthenticationPlugin(Map aut } if (pluginClassName != null) { - log.info("Authentication plugin class obtained from ZK: "+pluginClassName); + log.debug("Authentication plugin class obtained from security.json: "+pluginClassName); } else if (System.getProperty(AUTHENTICATION_PLUGIN_PROP) != null) { pluginClassName = System.getProperty(AUTHENTICATION_PLUGIN_PROP); - log.info("Authentication plugin class obtained from system property '" + + log.debug("Authentication plugin class obtained from system property '" + AUTHENTICATION_PLUGIN_PROP + "': " + pluginClassName); } else { log.debug("No authentication plugin used."); @@ -463,14 +462,11 @@ public void load() { MDCLoggingContext.setNode(this); - ZkStateReader.ConfigData securityConfig = isZooKeeperAware() ? getZkController().getZkStateReader().getSecurityProps(false) : new ZkStateReader.ConfigData(EMPTY_MAP, -1); - initializeAuthorizationPlugin((Map) securityConfig.data.get("authorization")); - initializeAuthenticationPlugin((Map) securityConfig.data.get("authentication")); - + securityConfHandler = isZooKeeperAware() ? new SecurityConfHandlerZk(this) : new SecurityConfHandlerLocal(this); + reloadSecurityProperties(); this.backupRepoFactory = new BackupRepositoryFactory(cfg.getBackupRepositoryPlugins()); containerHandlers.put(ZK_PATH, new ZookeeperInfoHandler(this)); - securityConfHandler = new SecurityConfHandler(this); collectionsHandler = createHandler(cfg.getCollectionsHandlerClass(), CollectionsHandler.class); containerHandlers.put(COLLECTIONS_HANDLER_PATH, collectionsHandler); infoHandler = createHandler(cfg.getInfoHandlerClass(), InfoHandler.class); @@ -567,10 +563,17 @@ public void load() { } public void securityNodeChanged() { - log.info("Security node changed"); - ZkStateReader.ConfigData securityConfig = getZkController().getZkStateReader().getSecurityProps(false); - initializeAuthorizationPlugin((Map) securityConfig.data.get("authorization")); - initializeAuthenticationPlugin((Map) securityConfig.data.get("authentication")); + log.info("Security node changed, reloading security.json"); + reloadSecurityProperties(); + } + + /** + * Make sure securityConfHandler is initialized + */ + private void reloadSecurityProperties() { + SecurityConfHandler.SecurityConfig securityConfig = securityConfHandler.getSecurityConfig(false); + initializeAuthorizationPlugin((Map) securityConfig.getData().get("authorization")); + initializeAuthenticationPlugin((Map) securityConfig.getData().get("authentication")); } private static void checkForDuplicateCoreNames(List cds) { diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java index 0f4dd7bc8477..51ae2259c063 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java @@ -17,6 +17,8 @@ package org.apache.solr.handler.admin; import java.io.IOException; +import java.io.InputStream; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; @@ -25,7 +27,6 @@ import java.util.Objects; import org.apache.solr.common.SolrException; -import org.apache.solr.common.cloud.ZkStateReader.ConfigData; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.util.Utils; import org.apache.solr.core.CoreContainer; @@ -37,10 +38,14 @@ import org.apache.solr.security.ConfigEditablePlugin; import org.apache.solr.security.PermissionNameProvider; import org.apache.solr.util.CommandOperation; -import org.apache.zookeeper.KeeperException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class SecurityConfHandler extends RequestHandlerBase implements PermissionNameProvider { - private CoreContainer cores; +import static org.apache.solr.common.SolrException.ErrorCode.SERVER_ERROR; + +public abstract class SecurityConfHandler extends RequestHandlerBase implements PermissionNameProvider { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + protected CoreContainer cores; public SecurityConfHandler(CoreContainer coreContainer) { this.cores = coreContainer; @@ -93,10 +98,11 @@ private void doEdit(SolrQueryRequest req, SolrQueryResponse rsp, String path, fi throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No commands"); } for (; ; ) { - ConfigData data = getSecurityProps(true); - Map latestConf = (Map) data.data.get(key); + SecurityConfig securityConfig = getSecurityConfig(true); + Map data = securityConfig.getData(); + Map latestConf = (Map) data.get(key); if (latestConf == null) { - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "No configuration present for " + key); + throw new SolrException(SERVER_ERROR, "No configuration present for " + key); } List commandsCopy = CommandOperation.clone(ops); Map out = configEditablePlugin.edit(Utils.getDeepCopy(latestConf, 4) , commandsCopy); @@ -106,20 +112,29 @@ private void doEdit(SolrQueryRequest req, SolrQueryResponse rsp, String path, fi rsp.add(CommandOperation.ERR_MSGS, errs); return; } - //no edits + log.debug("No edits made"); return; } else { if(!Objects.equals(latestConf.get("class") , out.get("class"))){ - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "class cannot be modified"); + throw new SolrException(SERVER_ERROR, "class cannot be modified"); } Map meta = getMapValue(out, ""); - meta.put("v", data.version+1);//encode the expected zkversion - data.data.put(key, out); - if(persistConf("/security.json", Utils.toJSON(data.data), data.version)) return; + meta.put("v", securityConfig.getVersion()+1);//encode the expected zkversion + data.put(key, out); + + if(persistConf(securityConfig)) { + securityConfEdited(); + return; + } } } } + /** + * Hook where you can do stuff after a config has been edited. Defaults to NOP + */ + protected void securityConfEdited() {} + Object getPlugin(String key) { Object plugin = null; if ("authentication".equals(key)) plugin = cores.getAuthenticationPlugin(); @@ -127,38 +142,14 @@ Object getPlugin(String key) { return plugin; } - ConfigData getSecurityProps(boolean getFresh) { - return cores.getZkController().getZkStateReader().getSecurityProps(getFresh); - } - - boolean persistConf(String path, byte[] buf, int version) { - try { - cores.getZkController().getZkClient().setData(path,buf,version, true); - return true; - } catch (KeeperException.BadVersionException bdve){ - return false; - } catch (Exception e) { - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, " Unable to persist conf",e); - } - } - - - private void getConf(SolrQueryResponse rsp, String key) { - ConfigData map = cores.getZkController().getZkStateReader().getSecurityProps(false); - Object o = map == null ? null : map.data.get(key); - if (o == null) { - rsp.add(CommandOperation.ERR_MSGS, Collections.singletonList("No " + key + " configured")); - } else { - rsp.add(key+".enabled", getPlugin(key)!=null); - rsp.add(key, o); - } - } + protected abstract void getConf(SolrQueryResponse rsp, String key); public static Map getMapValue(Map lookupMap, String key) { Map m = (Map) lookupMap.get(key); if (m == null) lookupMap.put(key, m = new LinkedHashMap<>()); return m; } + public static List getListValue(Map lookupMap, String key) { List l = (List) lookupMap.get(key); if (l == null) lookupMap.put(key, l= new ArrayList()); @@ -170,6 +161,77 @@ public String getDescription() { return "Edit or read security configuration"; } + /** + * Gets security.json from source + */ + public abstract SecurityConfig getSecurityConfig(boolean getFresh); + + /** + * Persist security.json to the source, optionally with a version + */ + protected abstract boolean persistConf(SecurityConfig securityConfig) throws IOException; + + /** + * Object to hold security.json as nested Map<String,Object> and optionally its version. + * The version property is optional and defaults to -1 if not initialized. + * The data object defaults to EMPTY_MAP if not set + */ + public static class SecurityConfig { + private Map data = Collections.EMPTY_MAP; + private int version = -1; + + public SecurityConfig() {} + + /** + * Sets the data as a Map + * @param data a Map + * @return SecurityConf object (builder pattern) + */ + public SecurityConfig setData(Map data) { + this.data = data; + return this; + } + /** + * Sets the data as an Object, but the object needs to be of type Map + * @param data an Object of type Map<String,Object> + * @return SecurityConf object (builder pattern) + */ + public SecurityConfig setData(Object data) { + if (data instanceof Map) { + this.data = (Map) data; + return this; + } else { + throw new SolrException(SERVER_ERROR, "Illegal format when parsing security.json, not object"); + } + } + + /** + * Sets version + * @param version integer for version. Depends on underlying storage + * @return SecurityConf object (builder pattern) + */ + public SecurityConfig setVersion(int version) { + this.version = version; + return this; + } + + public Map getData() { + return data; + } + + public int getVersion() { + return version; + } + + /** + * Set data from input stream + * @param securityJsonInputStream an input stream for security.json + * @return this (builder pattern) + */ + public SecurityConfig setData(InputStream securityJsonInputStream) { + return setData(Utils.fromJSON(securityJsonInputStream)); + } } +} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerLocal.java b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerLocal.java new file mode 100644 index 000000000000..59879148b6cd --- /dev/null +++ b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerLocal.java @@ -0,0 +1,101 @@ +/* + * 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.handler.admin; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.invoke.MethodHandles; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; + +import org.apache.solr.common.SolrException; +import org.apache.solr.common.util.Utils; +import org.apache.solr.core.CoreContainer; +import org.apache.solr.core.SolrResourceLoader; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.util.CommandOperation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Security Configuration Handler which works on standalone local files + */ +public class SecurityConfHandlerLocal extends SecurityConfHandler { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Path SECURITY_JSON_PATH = SolrResourceLoader.locateSolrHome().resolve("security.json"); + + public SecurityConfHandlerLocal(CoreContainer coreContainer) { + super(coreContainer); + } + + /** + * Fetches security props from SOLR_HOME + * @param getFresh NOP + * @return SecurityConfig whose data property either contains security.json, or an empty map if not found + */ + @Override + public SecurityConfig getSecurityConfig(boolean getFresh) { + if (Files.exists(SECURITY_JSON_PATH)) { + try (InputStream securityJsonIs = Files.newInputStream(SECURITY_JSON_PATH)) { + return new SecurityConfig().setData(securityJsonIs); + } catch (IOException e) { /* Fall through */ } + } + return new SecurityConfig(); + } + + @Override + protected void getConf(SolrQueryResponse rsp, String key) { + SecurityConfig props = getSecurityConfig(false); + Object o = props.getData().get(key); + if (o == null) { + rsp.add(CommandOperation.ERR_MSGS, Collections.singletonList("No " + key + " configured")); + } else { + rsp.add(key+".enabled", getPlugin(key)!=null); + rsp.add(key, o); + } + } + + @Override + protected boolean persistConf(SecurityConfig securityConfig) throws IOException { + if (securityConfig == null || securityConfig.getData().isEmpty()) { + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, + "Failed persisting security.json to SOLR_HOME. Object was empty."); + } + try(OutputStream securityJsonOs = Files.newOutputStream(SECURITY_JSON_PATH)) { + securityJsonOs.write(Utils.toJSON(securityConfig.getData())); + log.debug("Persisted security.json to {}", SECURITY_JSON_PATH); + return true; + } catch (IOException e) { + log.warn("Failed persisting security.json to {}", SECURITY_JSON_PATH, e); + return false; + } + } + + @Override + public String getDescription() { + return "Edit or read security configuration locally in SOLR_HOME"; + } + + @Override + protected void securityConfEdited() { + // Need to call explicitly since we will not get notified of changes to local security.json + cores.securityNodeChanged(); + } +} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerZk.java b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerZk.java new file mode 100644 index 000000000000..8323b8a1315a --- /dev/null +++ b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerZk.java @@ -0,0 +1,92 @@ +/* + * 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.handler.admin; + +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.Collections; + +import org.apache.solr.common.SolrException; +import org.apache.solr.common.cloud.ZkStateReader; +import org.apache.solr.common.util.Utils; +import org.apache.solr.core.CoreContainer; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.util.CommandOperation; +import org.apache.zookeeper.KeeperException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.solr.common.SolrException.ErrorCode.SERVER_ERROR; +import static org.apache.solr.common.cloud.ZkStateReader.SOLR_SECURITY_CONF_PATH; + +/** + * Security Configuration Handler which works with Zookeeper + */ +public class SecurityConfHandlerZk extends SecurityConfHandler { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + public SecurityConfHandlerZk(CoreContainer coreContainer) { + super(coreContainer); + } + + /** + * Fetches security props from Zookeeper and adds version + * @param getFresh refresh from ZK + * @return SecurityConfig whose data property either contains security.json, or an empty map if not found + */ + @Override + public SecurityConfig getSecurityConfig(boolean getFresh) { + ZkStateReader.ConfigData configDataFromZk = cores.getZkController().getZkStateReader().getSecurityProps(getFresh); + return configDataFromZk == null ? + new SecurityConfig() : + new SecurityConfig().setData(configDataFromZk.data).setVersion(configDataFromZk.version); + } + + @Override + protected void getConf(SolrQueryResponse rsp, String key) { + ZkStateReader.ConfigData map = cores.getZkController().getZkStateReader().getSecurityProps(false); + Object o = map == null ? null : map.data.get(key); + if (o == null) { + rsp.add(CommandOperation.ERR_MSGS, Collections.singletonList("No " + key + " configured")); + } else { + rsp.add(key+".enabled", getPlugin(key)!=null); + rsp.add(key, o); + } + } + + @Override + protected boolean persistConf(SecurityConfig securityConfig) throws IOException { + try { + cores.getZkController().getZkClient().setData(SOLR_SECURITY_CONF_PATH, + Utils.toJSON(securityConfig.getData()), + securityConfig.getVersion(), true); + log.debug("Persisted security.json to {}", SOLR_SECURITY_CONF_PATH); + return true; + } catch (KeeperException.BadVersionException bdve){ + log.warn("Failed persisting security.json to {}", SOLR_SECURITY_CONF_PATH, bdve); + return false; + } catch (Exception e) { + throw new SolrException(SERVER_ERROR, "Unable to persist security.json", e); + } + } + + @Override + public String getDescription() { + return "Edit or read security configuration from Zookeeper"; + } + +} diff --git a/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java b/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java index 49c02d72e0cb..29a887b94881 100644 --- a/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java +++ b/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java @@ -43,12 +43,12 @@ public class BasicAuthPlugin extends AuthenticationPlugin implements ConfigEditablePlugin { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - private AuthenticationProvider zkAuthentication; + private AuthenticationProvider authenticationProvider; private final static ThreadLocal
authHeader = new ThreadLocal<>(); private boolean blockUnknown = false; public boolean authenticate(String username, String pwd) { - return zkAuthentication.authenticate(username, pwd); + return authenticationProvider.authenticate(username, pwd); } @Override @@ -61,7 +61,7 @@ public void init(Map pluginConfig) { log.error(e.getMessage()); } } - zkAuthentication = getAuthenticationProvider(pluginConfig); + authenticationProvider = getAuthenticationProvider(pluginConfig); } @Override @@ -79,8 +79,8 @@ public Map edit(Map latestConf, List p } private void authenticationFailure(HttpServletResponse response, String message) throws IOException { - for (Map.Entry entry : zkAuthentication.getPromptHeaders().entrySet()) { + for (Map.Entry entry : authenticationProvider.getPromptHeaders().entrySet()) { response.setHeader(entry.getKey(), entry.getValue()); } response.sendError(401, message); @@ -143,7 +143,7 @@ public Principal getUserPrincipal() { if (blockUnknown) { authenticationFailure(response, "require authentication"); } else { - request.setAttribute(AuthenticationPlugin.class.getName(), zkAuthentication.getPromptHeaders()); + request.setAttribute(AuthenticationPlugin.class.getName(), authenticationProvider.getPromptHeaders()); filterChain.doFilter(request, response); return true; } diff --git a/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerLocalForTest.java b/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerLocalForTest.java new file mode 100644 index 000000000000..ffe2ea4c25d3 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerLocalForTest.java @@ -0,0 +1,39 @@ +/* + * 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.handler.admin; + +import java.io.IOException; + +import org.apache.solr.core.CoreContainer; + +/** + * Wrapper for use in tests + */ +public class SecurityConfHandlerLocalForTest extends SecurityConfHandlerLocal { + public SecurityConfHandlerLocalForTest(CoreContainer coreContainer) { + super(coreContainer); + } + + public boolean persistConf(SecurityConfig securityConfig) throws IOException { + return super.persistConf(securityConfig); + } + + public void securityConfEdited() { + super.securityConfEdited(); + } +} diff --git a/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerTest.java index 5e3d4071d24c..54c8587bfe7c 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerTest.java @@ -23,7 +23,6 @@ import java.util.Map; import org.apache.solr.SolrTestCaseJ4; -import org.apache.solr.common.cloud.ZkStateReader.ConfigData; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.common.util.Utils; @@ -34,6 +33,7 @@ import org.apache.solr.util.CommandOperation; import static org.apache.solr.common.util.Utils.makeMap; +import static org.apache.solr.handler.admin.SecurityConfHandler.SecurityConfig; public class SecurityConfHandlerTest extends SolrTestCaseJ4 { @@ -51,8 +51,8 @@ public void testEdit() throws Exception { handler.handleRequestBody(req,new SolrQueryResponse()); BasicAuthPlugin basicAuth = new BasicAuthPlugin(); - ConfigData securityCfg = (ConfigData) handler.m.get("/security.json"); - basicAuth.init((Map) securityCfg.data.get("authentication")); + SecurityConfig securityCfg = handler.m.get("/security.json"); + basicAuth.init((Map) securityCfg.getData().get("authentication")); assertTrue(basicAuth.authenticate("tom", "TomIsUberCool")); command = "{\n" + @@ -62,9 +62,9 @@ public void testEdit() throws Exception { o = new ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8),""); req.setContentStreams(Collections.singletonList(o)); handler.handleRequestBody(req,new SolrQueryResponse()); - securityCfg = (ConfigData) handler.m.get("/security.json"); - assertEquals(3, securityCfg.version); - Map result = (Map) securityCfg.data.get("authentication"); + securityCfg = handler.m.get("/security.json"); + assertEquals(3, securityCfg.getVersion()); + Map result = (Map) securityCfg.getData().get("authentication"); result = (Map) result.get("credentials"); assertTrue(result.isEmpty()); @@ -86,7 +86,7 @@ public void testEdit() throws Exception { SolrQueryResponse rsp = new SolrQueryResponse(); handler.handleRequestBody(req, rsp); assertNull(rsp.getValues().get(CommandOperation.ERR_MSGS)); - Map authzconf = (Map) ((ConfigData) handler.m.get("/security.json")).data.get("authorization"); + Map authzconf = (Map) handler.m.get("/security.json").getData().get("authorization"); Map userRoles = (Map) authzconf.get("user-role"); List tomRoles = (List) userRoles.get("tom"); assertTrue(tomRoles.contains("admin")); @@ -108,7 +108,7 @@ public void testEdit() throws Exception { req.setContentStreams(Collections.singletonList(o)); rsp = new SolrQueryResponse(); handler.handleRequestBody(req, rsp); - authzconf = (Map) ((ConfigData) handler.m.get("/security.json")).data.get("authorization"); + authzconf = (Map) handler.m.get("/security.json").getData().get("authorization"); permissions = (List) authzconf.get("permissions"); Map p = permissions.get(1); @@ -128,7 +128,7 @@ public void testEdit() throws Exception { req.setContentStreams(Collections.singletonList(o)); rsp = new SolrQueryResponse(); handler.handleRequestBody(req, rsp); - authzconf = (Map) ((ConfigData) handler.m.get("/security.json")).data.get("authorization"); + authzconf = (Map) handler.m.get("/security.json").getData().get("authorization"); permissions = (List) authzconf.get("permissions"); p = permissions.get(0); @@ -151,7 +151,7 @@ public void testEdit() throws Exception { rsp = new SolrQueryResponse(); handler.handleRequestBody(req, rsp); assertNull(rsp.getValues().get(CommandOperation.ERR_MSGS)); - authzconf = (Map) ((ConfigData) handler.m.get("/security.json")).data.get("authorization"); + authzconf = (Map) handler.m.get("/security.json").getData().get("authorization"); userRoles = (Map) authzconf.get("user-role"); assertEquals(0, userRoles.size()); permissions = (List) authzconf.get("permissions"); @@ -178,25 +178,26 @@ public void testEdit() throws Exception { public static class MockSecurityHandler extends SecurityConfHandler { - private Map m; + private Map m; final BasicAuthPlugin basicAuthPlugin = new BasicAuthPlugin(); final RuleBasedAuthorizationPlugin rulesBasedAuthorizationPlugin = new RuleBasedAuthorizationPlugin(); - public MockSecurityHandler() { + public MockSecurityHandler() { super(null); m = new HashMap<>(); - ConfigData data = new ConfigData(makeMap("authentication", makeMap("class", "solr."+ BasicAuthPlugin.class.getSimpleName())), 1); - data.data.put("authorization", makeMap("class", "solr."+RuleBasedAuthorizationPlugin.class.getSimpleName())); - m.put("/security.json", data); - + SecurityConfig sp = new SecurityConfig(); + sp.setData(makeMap("authentication", makeMap("class", "solr."+ BasicAuthPlugin.class.getSimpleName()))); + sp.setVersion(1); + sp.getData().put("authorization", makeMap("class", "solr."+RuleBasedAuthorizationPlugin.class.getSimpleName())); + m.put("/security.json", sp); basicAuthPlugin.init(new HashMap<>()); rulesBasedAuthorizationPlugin.init(new HashMap<>()); } - public Map getM() { + public Map getM() { return m; } @@ -212,24 +213,25 @@ Object getPlugin(String key) { } @Override - ConfigData getSecurityProps(boolean getFresh) { - return (ConfigData) m.get("/security.json"); + protected void getConf(SolrQueryResponse rsp, String key) { + // NOP + } + + @Override + public SecurityConfig getSecurityConfig(boolean getFresh) { + return m.get("/security.json"); } @Override - boolean persistConf(String key, byte[] buf, int version) { - Object data = m.get(key); - if (data instanceof ConfigData) { - ConfigData configData = (ConfigData) data; - if (configData.version == version) { - ConfigData result = new ConfigData((Map) Utils.fromJSON(buf), version + 1); - m.put(key, result); - return true; - } else { - return false; - } + protected boolean persistConf(SecurityConfig props) { + SecurityConfig fromMap = m.get("/security.json"); + if (fromMap.getVersion() == props.getVersion()) { + props.setVersion(props.getVersion()+1); + m.put("/security.json", props); + return true; + } else { + return false; } - throw new RuntimeException(); } @@ -254,7 +256,7 @@ public String getStandardJson() throws Exception { req.setContentStreams(Collections.singletonList(o)); SolrQueryResponse rsp = new SolrQueryResponse(); handleRequestBody(req, rsp); - Map data = ((ConfigData) m.get("/security.json")).data; + Map data = m.get("/security.json").getData(); ((Map)data.get("authentication")).remove(""); ((Map)data.get("authorization")).remove(""); return Utils.toJSONString (data); diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java index 6967b279d440..4134bf28eebc 100644 --- a/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java +++ b/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java @@ -292,11 +292,11 @@ public static Replica getRandomReplica(DocCollection coll, Random random) { return l.isEmpty() ? null : l.get(0); } - static final Predicate NOT_NULL_PREDICATE = o -> o != null; + protected static final Predicate NOT_NULL_PREDICATE = o -> o != null; //the password is 'SolrRocks' //this could be generated everytime. But , then we will not know if there is any regression - private static final String STD_CONF = "{\n" + + protected static final String STD_CONF = "{\n" + " 'authentication':{\n" + " 'class':'solr.BasicAuthPlugin',\n" + " 'credentials':{'solr':'orwp2Ghgj39lmnrZOTm7Qtre1VqHFDfwAEzr0ApbN3Y= Ju5osoAqOX8iafhWpPP01E5P+sg8tK8tHON7rCYZRRw='}},\n" + diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java new file mode 100644 index 000000000000..b158fe0eaeab --- /dev/null +++ b/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java @@ -0,0 +1,238 @@ +/* + * 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; + +import java.io.File; +import java.lang.invoke.MethodHandles; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Properties; +import java.util.Random; + +import org.apache.commons.io.FileUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.message.AbstractHttpMessage; +import org.apache.http.message.BasicHeader; +import org.apache.solr.client.solrj.SolrRequest; +import org.apache.solr.client.solrj.embedded.JettySolrRunner; +import org.apache.solr.client.solrj.impl.HttpClientUtil; +import org.apache.solr.client.solrj.impl.HttpSolrClient; +import org.apache.solr.client.solrj.request.GenericSolrRequest; +import org.apache.solr.common.cloud.DocCollection; +import org.apache.solr.common.cloud.Replica; +import org.apache.solr.common.cloud.Slice; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.util.Base64; +import org.apache.solr.common.util.ContentStreamBase; +import org.apache.solr.common.util.Utils; +import org.apache.solr.handler.admin.SecurityConfHandler; +import org.apache.solr.handler.admin.SecurityConfHandlerLocalForTest; +import org.apache.solr.util.AbstractSolrTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.apache.solr.security.BasicAuthIntegrationTest.NOT_NULL_PREDICATE; +import static org.apache.solr.security.BasicAuthIntegrationTest.STD_CONF; +import static org.apache.solr.security.BasicAuthIntegrationTest.verifySecurityStatus; + +public class BasicAuthStandaloneTest extends AbstractSolrTestCase { + + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final String CONF_DIR = "solr/configsets/configset-2/conf/"; + private static final String ROOT_DIR = "solr/"; + + SecurityConfHandlerLocalForTest securityConfHandler; + SolrInstance instance = null; + JettySolrRunner jetty; + + @Before + @Override + public void setUp() throws Exception + { + super.setUp(); + instance = new SolrInstance("inst", null); + instance.setUp(); + System.setProperty("solr.solr.home", instance.getHomeDir()); + jetty = createJetty(instance); + initCore("solrconfig.xml", "schema.xml", instance.getHomeDir()); + securityConfHandler = new SecurityConfHandlerLocalForTest(jetty.getCoreContainer()); + } + + @Override + @After + public void tearDown() throws Exception { + jetty.stop(); + super.tearDown(); + } + + @Test + public void testBasicAuth() throws Exception { + + String authcPrefix = "/admin/authentication"; + + HttpClient cl = null; + HttpSolrClient httpSolrClient = null; + try { + cl = HttpClientUtil.createClient(null); + String baseUrl = buildUrl(jetty.getLocalPort(), "/solr"); + httpSolrClient = getHttpSolrClient(baseUrl); + + verifySecurityStatus(cl, baseUrl + authcPrefix, "/errorMessages", null, 20); + + // Write security.json locally. Should cause security to be initialized + securityConfHandler.persistConf(new SecurityConfHandler.SecurityConfig() + .setData(Utils.fromJSONString(STD_CONF.replaceAll("'", "\"")))); + securityConfHandler.securityConfEdited(); + verifySecurityStatus(cl, baseUrl + authcPrefix, "authentication/class", "solr.BasicAuthPlugin", 20); + + String command = "{\n" + + "'set-user': {'harry':'HarryIsCool'}\n" + + "}"; + + GenericSolrRequest genericReq = new GenericSolrRequest(SolrRequest.METHOD.POST, authcPrefix, new ModifiableSolrParams()); + genericReq.setContentStreams(Collections.singletonList(new ContentStreamBase.ByteArrayStream(command.getBytes(UTF_8), ""))); + + HttpSolrClient finalHttpSolrClient = httpSolrClient; + HttpSolrClient.RemoteSolrException exp = expectThrows(HttpSolrClient.RemoteSolrException.class, () -> { + finalHttpSolrClient.request(genericReq); + }); + assertEquals(401, exp.code()); + + command = "{\n" + + "'set-user': {'harry':'HarryIsUberCool'}\n" + + "}"; + + HttpPost httpPost = new HttpPost(baseUrl + authcPrefix); + setBasicAuthHeader(httpPost, "solr", "SolrRocks"); + httpPost.setEntity(new ByteArrayEntity(command.getBytes(UTF_8))); + httpPost.addHeader("Content-Type", "application/json; charset=UTF-8"); + verifySecurityStatus(cl, baseUrl + authcPrefix, "authentication.enabled", "true", 20); + HttpResponse r = cl.execute(httpPost); + int statusCode = r.getStatusLine().getStatusCode(); + Utils.consumeFully(r.getEntity()); + assertEquals("proper_cred sent, but access denied", 200, statusCode); + + verifySecurityStatus(cl, baseUrl + authcPrefix, "authentication/credentials/harry", NOT_NULL_PREDICATE, 20); + + // Read file from SOLR_HOME and verify that it contains our new user + assertTrue(new String(Utils.toJSON(securityConfHandler.getSecurityConfig(false).getData())).contains("harry")); + } finally { + if (cl != null) { + HttpClientUtil.close(cl); + httpSolrClient.close(); + } + } + } + + public static void setBasicAuthHeader(AbstractHttpMessage httpMsg, String user, String pwd) { + String userPass = user + ":" + pwd; + String encoded = Base64.byteArrayToBase64(userPass.getBytes(UTF_8)); + httpMsg.setHeader(new BasicHeader("Authorization", "Basic " + encoded)); + log.info("Added Basic Auth security Header {}",encoded ); + } + + public static Replica getRandomReplica(DocCollection coll, Random random) { + ArrayList l = new ArrayList<>(); + + for (Slice slice : coll.getSlices()) { + for (Replica replica : slice.getReplicas()) { + l.add(replica); + } + } + Collections.shuffle(l, random); + return l.isEmpty() ? null : l.get(0); + } + + private JettySolrRunner createJetty(SolrInstance instance) throws Exception { + Properties nodeProperties = new Properties(); + nodeProperties.setProperty("solr.data.dir", instance.getDataDir()); + JettySolrRunner jetty = new JettySolrRunner(instance.getHomeDir(), nodeProperties, buildJettyConfig("/solr")); + jetty.start(); + return jetty; + } + + + private class SolrInstance { + String name; + Integer port; + File homeDir; + File confDir; + File dataDir; + + /** + * if masterPort is null, this instance is a master -- otherwise this instance is a slave, and assumes the master is + * on localhost at the specified port. + */ + public SolrInstance(String name, Integer port) { + this.name = name; + this.port = port; + } + + public String getHomeDir() { + return homeDir.toString(); + } + + public String getSchemaFile() { + return CONF_DIR + "schema.xml"; + } + + public String getConfDir() { + return confDir.toString(); + } + + public String getDataDir() { + return dataDir.toString(); + } + + public String getSolrConfigFile() { + return CONF_DIR + "solrconfig.xml"; + } + + public String getSolrXmlFile() { + return ROOT_DIR + "solr.xml"; + } + + + public void setUp() throws Exception { + homeDir = createTempDir("inst").toFile(); + dataDir = new File(homeDir + "/collection1", "data"); + confDir = new File(homeDir + "/collection1", "conf"); + + homeDir.mkdirs(); + dataDir.mkdirs(); + confDir.mkdirs(); + + FileUtils.copyFile(getFile(getSolrXmlFile()), new File(homeDir, "solr.xml")); + File f = new File(confDir, "solrconfig.xml"); + FileUtils.copyFile(getFile(getSolrConfigFile()), f); + f = new File(confDir, "schema.xml"); + + FileUtils.copyFile(getFile(getSchemaFile()), f); + + Files.createFile(homeDir.toPath().resolve("collection1/core.properties")); + } + + } +} From 166dca64a65a25ac07fb1255519cbddd5878a12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Thu, 13 Oct 2016 12:41:25 +0200 Subject: [PATCH 02/26] SOLR-9481: Avoid endless loop in case of errors in doEdit. Bail out with exception after 3 attempts. Fail hard if exception with local filesystem --- .../org/apache/solr/handler/admin/SecurityConfHandler.java | 4 +++- .../apache/solr/handler/admin/SecurityConfHandlerLocal.java | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java index 51ae2259c063..1fea4312adb1 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java @@ -97,7 +97,7 @@ private void doEdit(SolrQueryRequest req, SolrQueryResponse rsp, String path, fi if (ops == null) { throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No commands"); } - for (; ; ) { + for (int count = 1; count <= 3 ; count++ ) { SecurityConfig securityConfig = getSecurityConfig(true); Map data = securityConfig.getData(); Map latestConf = (Map) data.get(key); @@ -127,7 +127,9 @@ private void doEdit(SolrQueryRequest req, SolrQueryResponse rsp, String path, fi return; } } + log.debug("Security edit operation failed {} time(s)" + count); } + throw new SolrException(SERVER_ERROR, "Failed to persist security config after 3 attempts. Giving up"); } /** diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerLocal.java b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerLocal.java index 59879148b6cd..40849dbfaf9d 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerLocal.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerLocal.java @@ -82,9 +82,9 @@ protected boolean persistConf(SecurityConfig securityConfig) throws IOException securityJsonOs.write(Utils.toJSON(securityConfig.getData())); log.debug("Persisted security.json to {}", SECURITY_JSON_PATH); return true; - } catch (IOException e) { - log.warn("Failed persisting security.json to {}", SECURITY_JSON_PATH, e); - return false; + } catch (Exception e) { + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, + "Failed persisting security.json to " + SECURITY_JSON_PATH, e); } } From dd32e07bb7c53086aa33e8845d41a2e6db851084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Thu, 13 Oct 2016 14:27:56 +0200 Subject: [PATCH 03/26] SOLR-9640: Support PKI authentication in standalone mode --- .../org/apache/solr/core/CoreContainer.java | 12 ++++++++- .../security/PKIAuthenticationPlugin.java | 25 ++++++++++++++++++- .../solr/servlet/SolrDispatchFilter.java | 2 +- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java index 357bfd0dfd5a..2c0bdd263af1 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -458,7 +458,9 @@ public void load() { hostName = cfg.getNodeName(); zkSys.initZooKeeper(this, solrHome, cfg.getCloudConfig()); - if(isZooKeeperAware()) pkiAuthenticationPlugin = new PKIAuthenticationPlugin(this, zkSys.getZkController().getNodeName()); + pkiAuthenticationPlugin = isZooKeeperAware() ? + new PKIAuthenticationPlugin(this, zkSys.getZkController().getNodeName()) : + new PKIAuthenticationPlugin(this, getNodeNameLocal()); MDCLoggingContext.setNode(this); @@ -562,6 +564,14 @@ public void load() { } } + /* + Builds a node name to be used with PKIAuth. + // TODO: A bit hacky that we in standalone mode need to get properties from cloudConfig :-( + */ + private String getNodeNameLocal() { + return getConfig().getCloudConfig().getHost()+":"+getConfig().getCloudConfig().getSolrHostPort()+"_solr"; + } + public void securityNodeChanged() { log.info("Security node changed, reloading security.json"); reloadSecurityProperties(); diff --git a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java index 9de9e42e6188..0e46d92cc914 100644 --- a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java +++ b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java @@ -22,7 +22,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.lang.invoke.MethodHandles; +import java.net.URLDecoder; import java.nio.ByteBuffer; import java.security.Principal; import java.security.PublicKey; @@ -193,7 +195,12 @@ private static PKIHeaderData parseCipher(String cipher, PublicKey key) { } PublicKey getRemotePublicKey(String nodename) { - String url = cores.getZkController().getZkStateReader().getBaseUrlForNodeName(nodename); + String url; + if (cores.isZooKeeperAware()) { + url = cores.getZkController().getZkStateReader().getBaseUrlForNodeName(nodename); + } else { + url = getBaseUrlForNodeNameLocal(nodename); + } try { String uri = url + PATH + "?wt=json&omitHeader=true"; log.debug("Fetching fresh public key from : {}",uri); @@ -218,6 +225,22 @@ PublicKey getRemotePublicKey(String nodename) { } + private String getBaseUrlForNodeNameLocal(String nodeName) { + final int _offset = nodeName.indexOf("_"); + if (_offset < 0) { + throw new IllegalArgumentException("nodeName does not contain expected '_' seperator: " + nodeName); + } + final String hostAndPort = nodeName.substring(0,_offset); + try { + final String path = URLDecoder.decode(nodeName.substring(1+_offset), "UTF-8"); + // TODO: Hacky way of getting urlScheme when not in SolrCloud mode + String urlScheme = System.getProperty("solr.jetty.keystore") == null ? "http" : "https"; + return urlScheme + "://" + hostAndPort + (path.isEmpty() ? "" : ("/" + path)); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException("JVM Does not seem to support UTF-8", e); + } + } + @Override public SolrHttpClientBuilder getHttpClientBuilder(SolrHttpClientBuilder builder) { HttpClientUtil.addRequestInterceptor(interceptor); diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java index 241749a38681..9e57a6651378 100644 --- a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java +++ b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java @@ -348,7 +348,7 @@ private boolean authenticateRequest(ServletRequest request, ServletResponse resp return true; } else { try { - if (PKIAuthenticationPlugin.PATH.equals(((HttpServletRequest) request).getPathInfo())) return true; + if (PKIAuthenticationPlugin.PATH.equals(((HttpServletRequest) request).getServletPath())) return true; } catch (Exception e) { log.error("Unexpected error ", e); } From 6ee57523003f1b0cd138bac52fec24a6c6fcd33b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Wed, 26 Oct 2016 22:51:40 +0200 Subject: [PATCH 04/26] SOLR-9640: Added test case --- solr/CHANGES.txt | 3 +-- .../src/java/org/apache/solr/core/CoreContainer.java | 6 ++---- .../apache/solr/security/PKIAuthenticationPlugin.java | 4 ++-- .../solr/security/TestPKIAuthenticationPlugin.java | 11 +++++++++++ 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 47c57d809493..4b046056d6f3 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -168,8 +168,7 @@ New Features * SOLR-9670: Support SOLR_AUTHENTICATION_OPTS in solr.cmd (janhoy) -* SOLR-9481: Authentication and Authorization plugins now work in standalone mode if security.json is placed in - SOLR_HOME on every node. Editing config through API is supported but affects only that one node. (janhoy) +* SOLR-9640: Support PKI authentication in standalone mode (janhoy) Bug Fixes ---------------------- diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java index 2c96b17ae241..a54efb2af24c 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -565,10 +565,8 @@ public void load() { } } - /* - Builds a node name to be used with PKIAuth. - // TODO: A bit hacky that we in standalone mode need to get properties from cloudConfig :-( - */ + // Builds a node name to be used with PKIAuth. + // Could use SysProps "host" and "jetty.port" directly, but we got those in cloudConfig already... private String getNodeNameLocal() { return getConfig().getCloudConfig().getHost()+":"+getConfig().getCloudConfig().getSolrHostPort()+"_solr"; } diff --git a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java index 0e46d92cc914..acd5342351b5 100644 --- a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java +++ b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java @@ -225,7 +225,7 @@ PublicKey getRemotePublicKey(String nodename) { } - private String getBaseUrlForNodeNameLocal(String nodeName) { + protected String getBaseUrlForNodeNameLocal(String nodeName) { final int _offset = nodeName.indexOf("_"); if (_offset < 0) { throw new IllegalArgumentException("nodeName does not contain expected '_' seperator: " + nodeName); @@ -233,7 +233,7 @@ private String getBaseUrlForNodeNameLocal(String nodeName) { final String hostAndPort = nodeName.substring(0,_offset); try { final String path = URLDecoder.decode(nodeName.substring(1+_offset), "UTF-8"); - // TODO: Hacky way of getting urlScheme when not in SolrCloud mode + // TODO: Find a better way of resolving urlScheme when not using ZK? String urlScheme = System.getProperty("solr.jetty.keystore") == null ? "http" : "https"; return urlScheme + "://" + hostAndPort + (path.isEmpty() ? "" : ("/" + path)); } catch (UnsupportedEncodingException e) { diff --git a/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java b/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java index 1eb1d2105b18..876ba95360ff 100644 --- a/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java +++ b/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java @@ -36,6 +36,8 @@ import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.util.CryptoKeys; import org.easymock.EasyMock; +import org.junit.Test; + import static org.easymock.EasyMock.getCurrentArguments; public class TestPKIAuthenticationPlugin extends SolrTestCaseJ4 { @@ -148,6 +150,15 @@ PublicKey getRemotePublicKey(String nodename) { } + @Test + public void testGetBaseUrlForNodeNameLocal() { + final MockPKIAuthenticationPlugin mock = new MockPKIAuthenticationPlugin(null, "myName"); + assertEquals("http://my.host:9876/solr2", mock.getBaseUrlForNodeNameLocal("my.host:9876_solr2")); + System.setProperty("solr.jetty.keystore", "foo"); + assertEquals("https://my.host:9876/solr2", mock.getBaseUrlForNodeNameLocal("my.host:9876_solr2")); + System.clearProperty("solr.jetty.keystore"); + } + private HttpServletRequest createMockRequest(final AtomicReference
header) { HttpServletRequest mockReq = EasyMock.createMock(HttpServletRequest.class); EasyMock.reset(mockReq); From b96c10626ccaefa5a3d0088418d17ac78291a094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Wed, 26 Oct 2016 23:10:42 +0200 Subject: [PATCH 05/26] SOLR-9640: Separate method and test for resolveUrlScheme. Now looking in sysProp urlScheme too as a way of overriding the solr.jetty.keystore if necessary --- .../solr/security/PKIAuthenticationPlugin.java | 15 ++++++++++++++- .../security/TestPKIAuthenticationPlugin.java | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java index acd5342351b5..146bccb8c134 100644 --- a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java +++ b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java @@ -234,13 +234,26 @@ protected String getBaseUrlForNodeNameLocal(String nodeName) { try { final String path = URLDecoder.decode(nodeName.substring(1+_offset), "UTF-8"); // TODO: Find a better way of resolving urlScheme when not using ZK? - String urlScheme = System.getProperty("solr.jetty.keystore") == null ? "http" : "https"; + String urlScheme = resolveUrlScheme(); return urlScheme + "://" + hostAndPort + (path.isEmpty() ? "" : ("/" + path)); } catch (UnsupportedEncodingException e) { throw new IllegalStateException("JVM Does not seem to support UTF-8", e); } } + /** + * Resolve urlScheme first from sysProp "urlScheme", if not set or invalid value, peek at ssl sysProps + * @return "https" if SSL is enabled, else "http" + */ + protected static String resolveUrlScheme() { + String urlScheme = System.getProperty("urlScheme"); + if (urlScheme != null && urlScheme.matches("https?")) { + return urlScheme; + } else { + return System.getProperty("solr.jetty.keystore") == null ? "http" : "https"; + } + } + @Override public SolrHttpClientBuilder getHttpClientBuilder(SolrHttpClientBuilder builder) { HttpClientUtil.addRequestInterceptor(interceptor); diff --git a/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java b/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java index 876ba95360ff..2cb2de263571 100644 --- a/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java +++ b/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java @@ -159,6 +159,24 @@ public void testGetBaseUrlForNodeNameLocal() { System.clearProperty("solr.jetty.keystore"); } + @Test + public void testResolveUrlScheme() { + System.clearProperty("urlScheme"); + System.clearProperty("solr.jetty.keystore"); + assertEquals("http", MockPKIAuthenticationPlugin.resolveUrlScheme()); + System.setProperty("urlScheme", "http"); + assertEquals("http", MockPKIAuthenticationPlugin.resolveUrlScheme()); + System.setProperty("urlScheme", "https"); + assertEquals("https", MockPKIAuthenticationPlugin.resolveUrlScheme()); + System.setProperty("urlScheme", "ftp"); + System.clearProperty("solr.jetty.keystore"); + assertEquals("http", MockPKIAuthenticationPlugin.resolveUrlScheme()); + System.setProperty("solr.jetty.keystore", "foo"); + assertEquals("https", MockPKIAuthenticationPlugin.resolveUrlScheme()); + System.clearProperty("urlScheme"); + System.clearProperty("solr.jetty.keystore"); + } + private HttpServletRequest createMockRequest(final AtomicReference
header) { HttpServletRequest mockReq = EasyMock.createMock(HttpServletRequest.class); EasyMock.reset(mockReq); From 0bcd9f2be44e0f7599380dff158c281ff546514b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Wed, 26 Oct 2016 23:11:52 +0200 Subject: [PATCH 06/26] SOLR-9640: Fix precommit --- .../org/apache/solr/handler/admin/SecurityConfHandlerLocal.java | 1 - 1 file changed, 1 deletion(-) diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerLocal.java b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerLocal.java index 985a070fbb33..34a635f69b3f 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerLocal.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandlerLocal.java @@ -29,7 +29,6 @@ import org.apache.solr.common.SolrException; import org.apache.solr.common.util.Utils; import org.apache.solr.core.CoreContainer; -import org.apache.solr.core.SolrResourceLoader; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.util.CommandOperation; import org.slf4j.Logger; From b042b2b975a3ddfe5dde4e2d98aa3cfa22a3ee90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Wed, 26 Oct 2016 23:41:42 +0200 Subject: [PATCH 07/26] SOLR-9640: Remove unused class --- .../SecurityConfHandlerLocalForTest.java | 39 ------------------- 1 file changed, 39 deletions(-) delete mode 100644 solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerLocalForTest.java diff --git a/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerLocalForTest.java b/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerLocalForTest.java deleted file mode 100644 index ffe2ea4c25d3..000000000000 --- a/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerLocalForTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.handler.admin; - -import java.io.IOException; - -import org.apache.solr.core.CoreContainer; - -/** - * Wrapper for use in tests - */ -public class SecurityConfHandlerLocalForTest extends SecurityConfHandlerLocal { - public SecurityConfHandlerLocalForTest(CoreContainer coreContainer) { - super(coreContainer); - } - - public boolean persistConf(SecurityConfig securityConfig) throws IOException { - return super.persistConf(securityConfig); - } - - public void securityConfEdited() { - super.securityConfEdited(); - } -} From a0ef6bc41b77b31142b779462b708017e7b52d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Thu, 27 Oct 2016 12:02:55 +0200 Subject: [PATCH 08/26] SOLR-9640: Added distributed test for BasicAuth standalone --- .../security/BasicAuthDistributedTest.java | 117 ++++++++++++++++++ .../security/BasicAuthStandaloneTest.java | 6 +- 2 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java new file mode 100644 index 000000000000..88608b4b2d74 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java @@ -0,0 +1,117 @@ +/* + * 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; + +import java.nio.file.Files; +import java.nio.file.Paths; + +import org.apache.lucene.util.LuceneTestCase.Slow; +import org.apache.solr.BaseDistributedSearchTestCase; +import org.apache.solr.client.solrj.embedded.JettySolrRunner; +import org.apache.solr.client.solrj.impl.HttpSolrClient; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.common.util.Utils; +import org.apache.solr.handler.admin.SecurityConfHandler; +import org.apache.solr.handler.admin.SecurityConfHandlerLocalForTesting; +import org.junit.Test; + +import static org.apache.solr.security.BasicAuthIntegrationTest.STD_CONF; +import static org.apache.solr.security.BasicAuthIntegrationTest.verifySecurityStatus; + +/** + * Tests basicAuth in a multi shard env + */ +@Slow +public class BasicAuthDistributedTest extends BaseDistributedSearchTestCase { + public BasicAuthDistributedTest() { + super(); + schemaString = "schema.xml"; + } + + private SecurityConfHandlerLocalForTesting securityConfHandler; + + @Test + public void test() throws Exception { + index(); + testAuth(); + } + + private void index() throws Exception { + del("*:*"); + indexr(id, "1", "text", "doc one"); + indexr(id, "2", "text", "doc two"); + indexr(id, "3", "text", "doc three"); + indexr(id, "4", "text", "doc four"); + indexr(id, "5", "text", "doc five"); + + commit(); // try to ensure there's more than one segment + + indexr(id, "6", "text", "doc six"); + indexr(id, "7", "text", "doc seven"); + indexr(id, "8", "text", "doc eight"); + indexr(id, "9", "text", "doc nine"); + indexr(id, "10", "text", "doc ten"); + + commit(); + + handle.clear(); + handle.put("QTime", SKIPVAL); + handle.put("timestamp", SKIPVAL); + handle.put("maxScore", SKIPVAL); + handle.put("_version_", SKIPVAL); + Thread.sleep(1000); + } + + private void testAuth() throws Exception { + System.out.println("Got "+jettys.size()+" jettys and "+ clients.size() +" clients"); + QueryResponse rsp = query("q","text:doc", "fl", "id,text", "sort", "id asc"); + assertEquals(10, rsp.getResults().getNumFound()); + + for (JettySolrRunner j : jettys) { + securityConfHandler = new SecurityConfHandlerLocalForTesting(j.getCoreContainer()); + securityConfHandler.persistConf(new SecurityConfHandler.SecurityConfig() + .setData(Utils.fromJSONString(ALL_CONF.replaceAll("'", "\"")))); + j.getCoreContainer().securityNodeChanged(); + } + + HttpSolrClient.RemoteSolrException expected = expectThrows(HttpSolrClient.RemoteSolrException.class, () -> { + query("q","text:doc", "fl", "id,text", "sort", "id asc"); + }); + assertEquals(401, expected.code()); + + // TODO: Query with auth + + // Remove auth again + for (JettySolrRunner j : jettys) { + securityConfHandler = new SecurityConfHandlerLocalForTesting(j.getCoreContainer()); + Files.delete(Paths.get(j.getSolrHome()).resolve("security.json")); + j.getCoreContainer().securityNodeChanged(); + } + + } + + protected static final String ALL_CONF = "{\n" + + " 'authentication':{\n" + + " 'blockUnknown':true,\n" + + " 'class':'solr.BasicAuthPlugin',\n" + + " 'credentials':{'solr':'orwp2Ghgj39lmnrZOTm7Qtre1VqHFDfwAEzr0ApbN3Y= Ju5osoAqOX8iafhWpPP01E5P+sg8tK8tHON7rCYZRRw='}},\n" + + " 'authorization':{\n" + + " 'class':'solr.RuleBasedAuthorizationPlugin',\n" + + " 'user-role':{'solr':'admin'},\n" + + " 'permissions':[{'name':'all','role':'admin'}]}}"; + +} diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java index 33c0ab31a60d..80c75e38a884 100644 --- a/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java +++ b/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java @@ -30,6 +30,7 @@ import org.apache.http.entity.ByteArrayEntity; import org.apache.http.message.AbstractHttpMessage; import org.apache.http.message.BasicHeader; +import org.apache.solr.BaseDistributedSearchTestCase; import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.embedded.JettySolrRunner; import org.apache.solr.client.solrj.impl.HttpClientUtil; @@ -53,7 +54,7 @@ import static org.apache.solr.security.BasicAuthIntegrationTest.STD_CONF; import static org.apache.solr.security.BasicAuthIntegrationTest.verifySecurityStatus; -public class BasicAuthStandaloneTest extends AbstractSolrTestCase { +public class BasicAuthStandaloneTest extends BaseDistributedSearchTestCase { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private Path ROOT_DIR = Paths.get(getSolrHome()); @@ -71,7 +72,8 @@ public void setUp() throws Exception instance = new SolrInstance("inst", null); instance.setUp(); jetty = createJetty(instance); - initCore("solrconfig.xml", "schema.xml", instance.getHomeDir().toString()); + assertTrue(jetty.isRunning()); + //initCore("solrconfig.xml", "schema.xml", instance.getHomeDir().toString()); securityConfHandler = new SecurityConfHandlerLocalForTesting(jetty.getCoreContainer()); } From 0ee40486b4707eb4371287f676e257116660ca42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Thu, 27 Oct 2016 14:24:18 +0200 Subject: [PATCH 09/26] SOLR-9640: Distributed test case now works --- .../security/BasicAuthDistributedTest.java | 46 ++++++++++++++----- .../solr/BaseDistributedSearchTestCase.java | 37 +++++++++++++-- 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java index 88608b4b2d74..82077fe772e6 100644 --- a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java +++ b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java @@ -16,6 +16,7 @@ */ package org.apache.solr.security; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; @@ -23,13 +24,18 @@ import org.apache.solr.BaseDistributedSearchTestCase; import org.apache.solr.client.solrj.embedded.JettySolrRunner; import org.apache.solr.client.solrj.impl.HttpSolrClient; +import org.apache.solr.client.solrj.request.QueryRequest; import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.util.Utils; +import org.apache.solr.core.CoreContainer; import org.apache.solr.handler.admin.SecurityConfHandler; import org.apache.solr.handler.admin.SecurityConfHandlerLocalForTesting; +import org.apache.solr.request.SolrQueryRequest; import org.junit.Test; import static org.apache.solr.security.BasicAuthIntegrationTest.STD_CONF; +import static org.apache.solr.security.BasicAuthIntegrationTest.setBasicAuthHeader; import static org.apache.solr.security.BasicAuthIntegrationTest.verifySecurityStatus; /** @@ -77,15 +83,12 @@ private void index() throws Exception { } private void testAuth() throws Exception { - System.out.println("Got "+jettys.size()+" jettys and "+ clients.size() +" clients"); QueryResponse rsp = query("q","text:doc", "fl", "id,text", "sort", "id asc"); assertEquals(10, rsp.getResults().getNumFound()); + // Enable authentication for (JettySolrRunner j : jettys) { - securityConfHandler = new SecurityConfHandlerLocalForTesting(j.getCoreContainer()); - securityConfHandler.persistConf(new SecurityConfHandler.SecurityConfig() - .setData(Utils.fromJSONString(ALL_CONF.replaceAll("'", "\"")))); - j.getCoreContainer().securityNodeChanged(); + writeSecurityJson(j.getCoreContainer()); } HttpSolrClient.RemoteSolrException expected = expectThrows(HttpSolrClient.RemoteSolrException.class, () -> { @@ -93,17 +96,38 @@ private void testAuth() throws Exception { }); assertEquals(401, expected.code()); - // TODO: Query with auth + // Add auth + ModifiableSolrParams params = new ModifiableSolrParams(); + params.add("q", "text:doc").add("fl", "id,text").add("sort", "id asc"); + QueryRequest req = new QueryRequest(params); + req.setBasicAuthCredentials("solr", "SolrRocks"); + rsp = req.process(clients.get(0), null); + if (jettys.size() > 1) { + assertTrue(rsp.getResults().getNumFound() < 10); + rsp = query(true, params, "solr", "SolrRocks"); + } + assertEquals(10, rsp.getResults().getNumFound()); - // Remove auth again + // Disable auth for (JettySolrRunner j : jettys) { - securityConfHandler = new SecurityConfHandlerLocalForTesting(j.getCoreContainer()); - Files.delete(Paths.get(j.getSolrHome()).resolve("security.json")); - j.getCoreContainer().securityNodeChanged(); + deleteSecurityJson(j.getCoreContainer()); } } - + + private void deleteSecurityJson(CoreContainer coreContainer) throws IOException { + securityConfHandler = new SecurityConfHandlerLocalForTesting(coreContainer); + Files.delete(Paths.get(coreContainer.getSolrHome()).resolve("security.json")); + coreContainer.securityNodeChanged(); + } + + private void writeSecurityJson(CoreContainer coreContainer) throws IOException { + securityConfHandler = new SecurityConfHandlerLocalForTesting(coreContainer); + securityConfHandler.persistConf(new SecurityConfHandler.SecurityConfig() + .setData(Utils.fromJSONString(ALL_CONF.replaceAll("'", "\"")))); + coreContainer.securityNodeChanged(); + } + protected static final String ALL_CONF = "{\n" + " 'authentication':{\n" + " 'blockUnknown':true,\n" + diff --git a/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java b/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java index 8c6eb6093dab..05bfb924d1bc 100644 --- a/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java +++ b/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java @@ -50,6 +50,7 @@ import org.apache.solr.client.solrj.embedded.JettyConfig; import org.apache.solr.client.solrj.embedded.JettySolrRunner; import org.apache.solr.client.solrj.impl.HttpSolrClient; +import org.apache.solr.client.solrj.request.QueryRequest; import org.apache.solr.client.solrj.request.UpdateRequest; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.response.UpdateResponse; @@ -558,6 +559,12 @@ protected QueryResponse queryServer(ModifiableSolrParams params) throws SolrServ return rsp; } + protected QueryResponse queryServer(QueryRequest req) throws IOException, SolrServerException { + int which = r.nextInt(clients.size()); + SolrClient client = clients.get(which); + return req.process(client, null); + } + /** * Sets distributed params. * Returns the QueryResponse from {@link #queryServer}, @@ -591,18 +598,31 @@ protected QueryResponse query(boolean setDistribParams, Object[] q) throws Excep * Returns the QueryResponse from {@link #queryServer} */ protected QueryResponse query(boolean setDistribParams, SolrParams p) throws Exception { + return query(setDistribParams, p, null, null); + } + + /** + * Returns the QueryResponse from {@link #queryServer} + * @param setDistribParams whether to do a distributed request + * @param user basic auth username (set to null if not in use) + * @param pass basic auth password (set to null if not in use) + * @return the query response + */ + protected QueryResponse query(boolean setDistribParams, SolrParams p, String user, String pass) throws Exception { final ModifiableSolrParams params = new ModifiableSolrParams(p); // TODO: look into why passing true causes fails params.set("distrib", "false"); - final QueryResponse controlRsp = controlClient.query(params); + QueryRequest req = generateQueryRequest(params, user, pass); + final QueryResponse controlRsp = req.process(controlClient, null); validateControlData(controlRsp); params.remove("distrib"); if (setDistribParams) setDistributedParams(params); + req = generateQueryRequest(params, user, pass); - QueryResponse rsp = queryServer(params); + QueryResponse rsp = queryServer(req); compareResponses(rsp, controlRsp); @@ -617,7 +637,8 @@ public void run() { int which = r.nextInt(clients.size()); SolrClient client = clients.get(which); try { - QueryResponse rsp = client.query(new ModifiableSolrParams(params)); + QueryRequest qreq = generateQueryRequest(new ModifiableSolrParams(params), user, pass); + QueryResponse rsp = qreq.process(client, null); if (verifyStress) { compareResponses(rsp, controlRsp); } @@ -636,7 +657,15 @@ public void run() { } return rsp; } - + + private QueryRequest generateQueryRequest(ModifiableSolrParams params, String user, String pass) { + QueryRequest req = new QueryRequest(params); + if (user != null && pass != null) { + req.setBasicAuthCredentials(user, pass); + } + return req; + } + public QueryResponse queryAndCompare(SolrParams params, SolrClient... clients) throws SolrServerException, IOException { return queryAndCompare(params, Arrays.asList(clients)); } From 328c818d4554d48b45bf0161d4e971e9a8213caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Thu, 27 Oct 2016 14:34:36 +0200 Subject: [PATCH 10/26] SOLR-9640: Remove sleep and isRunning assert etc --- .../test/org/apache/solr/security/BasicAuthDistributedTest.java | 1 - .../test/org/apache/solr/security/BasicAuthStandaloneTest.java | 2 -- 2 files changed, 3 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java index 82077fe772e6..bb50dd6175ff 100644 --- a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java +++ b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java @@ -79,7 +79,6 @@ private void index() throws Exception { handle.put("timestamp", SKIPVAL); handle.put("maxScore", SKIPVAL); handle.put("_version_", SKIPVAL); - Thread.sleep(1000); } private void testAuth() throws Exception { diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java index 80c75e38a884..f8a5519238c7 100644 --- a/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java +++ b/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java @@ -72,8 +72,6 @@ public void setUp() throws Exception instance = new SolrInstance("inst", null); instance.setUp(); jetty = createJetty(instance); - assertTrue(jetty.isRunning()); - //initCore("solrconfig.xml", "schema.xml", instance.getHomeDir().toString()); securityConfHandler = new SecurityConfHandlerLocalForTesting(jetty.getCoreContainer()); } From 8c7c2ac4ce5559b06b011f76c4d7877e382e3496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Thu, 27 Oct 2016 14:35:26 +0200 Subject: [PATCH 11/26] SOLR-9640: Remove unused imports --- .../org/apache/solr/security/BasicAuthDistributedTest.java | 5 ----- .../org/apache/solr/security/BasicAuthStandaloneTest.java | 1 - 2 files changed, 6 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java index bb50dd6175ff..ae728b0eb300 100644 --- a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java +++ b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java @@ -31,13 +31,8 @@ import org.apache.solr.core.CoreContainer; import org.apache.solr.handler.admin.SecurityConfHandler; import org.apache.solr.handler.admin.SecurityConfHandlerLocalForTesting; -import org.apache.solr.request.SolrQueryRequest; import org.junit.Test; -import static org.apache.solr.security.BasicAuthIntegrationTest.STD_CONF; -import static org.apache.solr.security.BasicAuthIntegrationTest.setBasicAuthHeader; -import static org.apache.solr.security.BasicAuthIntegrationTest.verifySecurityStatus; - /** * Tests basicAuth in a multi shard env */ diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java index f8a5519238c7..774cf8db421e 100644 --- a/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java +++ b/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java @@ -42,7 +42,6 @@ import org.apache.solr.common.util.Utils; import org.apache.solr.handler.admin.SecurityConfHandler; import org.apache.solr.handler.admin.SecurityConfHandlerLocalForTesting; -import org.apache.solr.util.AbstractSolrTestCase; import org.junit.After; import org.junit.Before; import org.junit.Test; From 985e2223f8e48b8086306d0b8a6d7c6723d39d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Wed, 2 Nov 2016 13:51:03 +0100 Subject: [PATCH 12/26] SOLR-9640: Revert unnecessary change to BasicAuthStandaloneTest --- .../test/org/apache/solr/security/BasicAuthStandaloneTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java index 50c86c1b380b..7d6c4363e033 100644 --- a/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java +++ b/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java @@ -30,7 +30,6 @@ import org.apache.http.entity.ByteArrayEntity; import org.apache.http.message.AbstractHttpMessage; import org.apache.http.message.BasicHeader; -import org.apache.solr.BaseDistributedSearchTestCase; import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.embedded.JettySolrRunner; import org.apache.solr.client.solrj.impl.HttpClientUtil; @@ -55,7 +54,7 @@ import static org.apache.solr.security.BasicAuthIntegrationTest.STD_CONF; import static org.apache.solr.security.BasicAuthIntegrationTest.verifySecurityStatus; -public class BasicAuthStandaloneTest extends BaseDistributedSearchTestCase { +public class BasicAuthStandaloneTest extends AbstractSolrTestCase { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private Path ROOT_DIR = Paths.get(getSolrHome()); From 586fd4b56e494a4bea644d268301503a9eda2add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Wed, 2 Nov 2016 16:19:59 +0100 Subject: [PATCH 13/26] SOLR-9640: Added debugging for /info/key/ --- .../solr/security/PKIAuthenticationPlugin.java | 7 ++++--- .../java/org/apache/solr/servlet/HttpSolrCall.java | 4 ++-- .../org/apache/solr/servlet/SolrDispatchFilter.java | 13 ++++++++----- .../solr/security/BasicAuthDistributedTest.java | 4 +++- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java index 146bccb8c134..c1095a9bfb30 100644 --- a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java +++ b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java @@ -195,14 +195,15 @@ private static PKIHeaderData parseCipher(String cipher, PublicKey key) { } PublicKey getRemotePublicKey(String nodename) { - String url; + String url, uri = null; + log.info("getRemotePublicKey for nodename {}", nodename); if (cores.isZooKeeperAware()) { url = cores.getZkController().getZkStateReader().getBaseUrlForNodeName(nodename); } else { url = getBaseUrlForNodeNameLocal(nodename); } try { - String uri = url + PATH + "?wt=json&omitHeader=true"; + uri = url + PATH + "?wt=json&omitHeader=true"; log.debug("Fetching fresh public key from : {}",uri); HttpResponse rsp = cores.getUpdateShardHandler().getHttpClient() .execute(new HttpGet(uri), HttpClientUtil.createNewHttpClientRequestContext()); @@ -219,7 +220,7 @@ PublicKey getRemotePublicKey(String nodename) { keyCache.put(nodename, pubKey); return pubKey; } catch (Exception e) { - log.error("Exception trying to get public key from : " + url, e); + log.error("Exception trying to get public key from : " + uri, e); return null; } diff --git a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java index c41595e8bb8b..41a7d32e3aa8 100644 --- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java +++ b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java @@ -492,7 +492,7 @@ public Action call() throws IOException { } private boolean shouldAuthorize() { - if(PKIAuthenticationPlugin.PATH.equals(path)) return false; + if(path != null && path.endsWith(PKIAuthenticationPlugin.PATH)) return false; //admin/info/key is the path where public key is exposed . it is always unsecured if (cores.getPkiAuthenticationPlugin() != null && req.getUserPrincipal() != null) { boolean b = cores.getPkiAuthenticationPlugin().needsAuthorization(req); @@ -1007,7 +1007,7 @@ public String toString() { response.delete(response.length() - 1, response.length()); response.append("], Path: [").append(resource).append("]"); - response.append(" path : ").append(path).append(" params :").append(solrReq.getParams()); + response.append(" path : ").append(path).append(" params :").append(solrReq == null ? null : solrReq.getParams()); return response.toString(); } diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java index 5a4cfb627fe5..a46e2691e233 100644 --- a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java +++ b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java @@ -348,11 +348,14 @@ private boolean authenticateRequest(ServletRequest request, ServletResponse resp if (authenticationPlugin == null) { return true; } else { - // /admin/info/key must be always open. see SOLR-9188 - // tests work only w/ getPathInfo - //otherwise it's just enough to have getServletPath() - if (PKIAuthenticationPlugin.PATH.equals(((HttpServletRequest) request).getServletPath()) || - PKIAuthenticationPlugin.PATH.equals(((HttpServletRequest) request).getPathInfo())) return true; + String requestUri = ((HttpServletRequest) request).getRequestURI(); + log.info("authenticateRequest uri={}", requestUri); + if (requestUri != null && requestUri.endsWith(PKIAuthenticationPlugin.PATH)) { + log.info("/info/key passthrogh"); + return true; // Let /admin/info/key through + } else { + log.info("/info/key FAILED requestUri.endsWith(PKIAuthenticationPlugin.PATH)={}", requestUri.endsWith(PKIAuthenticationPlugin.PATH)); + } String header = ((HttpServletRequest) request).getHeader(PKIAuthenticationPlugin.HEADER); if (header != null && cores.getPkiAuthenticationPlugin() != null) authenticationPlugin = cores.getPkiAuthenticationPlugin(); diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java index ae728b0eb300..5eeb7947fe98 100644 --- a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java +++ b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java @@ -31,6 +31,7 @@ import org.apache.solr.core.CoreContainer; import org.apache.solr.handler.admin.SecurityConfHandler; import org.apache.solr.handler.admin.SecurityConfHandlerLocalForTesting; +import org.apache.solr.util.LogLevel; import org.junit.Test; /** @@ -46,6 +47,7 @@ public BasicAuthDistributedTest() { private SecurityConfHandlerLocalForTesting securityConfHandler; @Test + @LogLevel("org.apache.solr=DEBUG") public void test() throws Exception { index(); testAuth(); @@ -86,7 +88,7 @@ private void testAuth() throws Exception { } HttpSolrClient.RemoteSolrException expected = expectThrows(HttpSolrClient.RemoteSolrException.class, () -> { - query("q","text:doc", "fl", "id,text", "sort", "id asc"); + query("q","text:doc-fail", "fl", "id,text", "sort", "id asc"); }); assertEquals(401, expected.code()); From fdf35aad1a7ef52478ee75623b2fece3d2e3b194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Wed, 22 Feb 2017 22:58:41 +0100 Subject: [PATCH 14/26] SOLR-9640: Fixes after merge master, move changes to 6.5 --- solr/CHANGES.txt | 4 ++-- .../apache/solr/security/TestPKIAuthenticationPlugin.java | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 76f0d2704cd3..a6befa67ea52 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -134,6 +134,8 @@ New Features field must both be stored=false, indexed=false, docValues=true. (Ishan Chattopadhyaya, hossman, noble, shalin, yonik) +* SOLR-9640: Support PKI authentication in standalone mode (janhoy) + Bug Fixes ---------------------- @@ -755,8 +757,6 @@ New Features * SOLR-1085: Add support for MoreLikeThis queries and responses in SolrJ client. (Maurice Jumelet, Bill Mitchell, Cao Manh Dat via shalin) -* SOLR-9640: Support PKI authentication in standalone mode (janhoy) - Bug Fixes ---------------------- diff --git a/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java b/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java index 1fe073e4f645..5fa2e0def56e 100644 --- a/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java +++ b/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java @@ -35,10 +35,11 @@ import org.apache.solr.request.SolrRequestInfo; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.util.CryptoKeys; -import static org.mockito.Mockito.*; import org.junit.Test; -import static org.easymock.EasyMock.getCurrentArguments; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class TestPKIAuthenticationPlugin extends SolrTestCaseJ4 { From 48adabb7f2fdb2df5ba21f3fd562669a2d07a096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Fri, 24 Feb 2017 13:14:24 +0100 Subject: [PATCH 15/26] SOLR-9640: Remove whitespace --- .../src/java/org/apache/solr/BaseDistributedSearchTestCase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java b/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java index 05bfb924d1bc..f11111edb59e 100644 --- a/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java +++ b/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java @@ -606,7 +606,7 @@ protected QueryResponse query(boolean setDistribParams, SolrParams p) throws Exc * @param setDistribParams whether to do a distributed request * @param user basic auth username (set to null if not in use) * @param pass basic auth password (set to null if not in use) - * @return the query response + * @return the query response */ protected QueryResponse query(boolean setDistribParams, SolrParams p, String user, String pass) throws Exception { From adfb55166188be462a69d3a6700aa3c6f90c73cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Fri, 24 Feb 2017 13:27:28 +0100 Subject: [PATCH 16/26] SOLR-9640: Remove whitespace --- .../apache/solr/security/TestPKIAuthenticationPlugin.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java b/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java index 5fa2e0def56e..5ca3d535dd82 100644 --- a/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java +++ b/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java @@ -145,10 +145,6 @@ PublicKey getRemotePublicKey(String nodename) { mock1.doAuthenticate(mockReq, null,filterChain ); assertNotNull(wrappedRequestByFilter.get()); assertEquals("$", ((HttpServletRequest) wrappedRequestByFilter.get()).getUserPrincipal().getName()); - - - - } @Test @@ -159,7 +155,7 @@ public void testGetBaseUrlForNodeNameLocal() { assertEquals("https://my.host:9876/solr2", mock.getBaseUrlForNodeNameLocal("my.host:9876_solr2")); System.clearProperty("solr.jetty.keystore"); } - + @Test public void testResolveUrlScheme() { System.clearProperty("urlScheme"); @@ -177,7 +173,7 @@ public void testResolveUrlScheme() { System.clearProperty("urlScheme"); System.clearProperty("solr.jetty.keystore"); } - + private HttpServletRequest createMockRequest(final AtomicReference
header) { HttpServletRequest mockReq = mock(HttpServletRequest.class); when(mockReq.getHeader(any(String.class))).then(invocation -> { From 02fc732ce708c6eb3b78ab6dfced32e62821526f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Fri, 24 Feb 2017 13:29:59 +0100 Subject: [PATCH 17/26] SOLR-9640: Remove whitespace --- .../org/apache/solr/security/BasicAuthDistributedTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java index 5eeb7947fe98..d78044c83caa 100644 --- a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java +++ b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java @@ -77,7 +77,7 @@ private void index() throws Exception { handle.put("maxScore", SKIPVAL); handle.put("_version_", SKIPVAL); } - + private void testAuth() throws Exception { QueryResponse rsp = query("q","text:doc", "fl", "id,text", "sort", "id asc"); assertEquals(10, rsp.getResults().getNumFound()); @@ -91,7 +91,7 @@ private void testAuth() throws Exception { query("q","text:doc-fail", "fl", "id,text", "sort", "id asc"); }); assertEquals(401, expected.code()); - + // Add auth ModifiableSolrParams params = new ModifiableSolrParams(); params.add("q", "text:doc").add("fl", "id,text").add("sort", "id asc"); @@ -103,7 +103,7 @@ private void testAuth() throws Exception { rsp = query(true, params, "solr", "SolrRocks"); } assertEquals(10, rsp.getResults().getNumFound()); - + // Disable auth for (JettySolrRunner j : jettys) { deleteSecurityJson(j.getCoreContainer()); @@ -133,5 +133,4 @@ private void writeSecurityJson(CoreContainer coreContainer) throws IOException { " 'class':'solr.RuleBasedAuthorizationPlugin',\n" + " 'user-role':{'solr':'admin'},\n" + " 'permissions':[{'name':'all','role':'admin'}]}}"; - } From 03f92343895c8dc66ffb93d83eaa1c5c0997e55d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Fri, 24 Feb 2017 13:33:10 +0100 Subject: [PATCH 18/26] SOLR-9640: Remove whitespace --- .../java/org/apache/solr/BaseDistributedSearchTestCase.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java b/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java index f11111edb59e..bbfc048a2c5f 100644 --- a/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java +++ b/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java @@ -564,7 +564,7 @@ protected QueryResponse queryServer(QueryRequest req) throws IOException, SolrSe SolrClient client = clients.get(which); return req.process(client, null); } - + /** * Sets distributed params. * Returns the QueryResponse from {@link #queryServer}, @@ -663,7 +663,7 @@ private QueryRequest generateQueryRequest(ModifiableSolrParams params, String us if (user != null && pass != null) { req.setBasicAuthCredentials(user, pass); } - return req; + return req; } public QueryResponse queryAndCompare(SolrParams params, SolrClient... clients) throws SolrServerException, IOException { From 903c1ff80811d91bbaccb2347ae9ae874db0f9cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Fri, 24 Feb 2017 13:33:41 +0100 Subject: [PATCH 19/26] SOLR-9640: Remove whitespace --- .../test/org/apache/solr/security/BasicAuthDistributedTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java index d78044c83caa..e35e369dac8c 100644 --- a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java +++ b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java @@ -82,7 +82,7 @@ private void testAuth() throws Exception { QueryResponse rsp = query("q","text:doc", "fl", "id,text", "sort", "id asc"); assertEquals(10, rsp.getResults().getNumFound()); - // Enable authentication + // Enable authentication for (JettySolrRunner j : jettys) { writeSecurityJson(j.getCoreContainer()); } From 08e13b0743ff627e55414b9e19b72d72deb523dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Wed, 4 Apr 2018 12:39:35 +0200 Subject: [PATCH 20/26] Extract mock class --- .../security/MockPKIAuthenticationPlugin.java | 54 +++++++++++++++++++ .../security/TestPKIAuthenticationPlugin.java | 33 +----------- 2 files changed, 56 insertions(+), 31 deletions(-) create mode 100644 solr/core/src/test/org/apache/solr/security/MockPKIAuthenticationPlugin.java diff --git a/solr/core/src/test/org/apache/solr/security/MockPKIAuthenticationPlugin.java b/solr/core/src/test/org/apache/solr/security/MockPKIAuthenticationPlugin.java new file mode 100644 index 000000000000..aa768b977ffd --- /dev/null +++ b/solr/core/src/test/org/apache/solr/security/MockPKIAuthenticationPlugin.java @@ -0,0 +1,54 @@ +/* + * 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; + +import java.security.PublicKey; +import java.util.HashMap; +import java.util.Map; + +import org.apache.solr.core.CoreContainer; +import org.apache.solr.request.SolrRequestInfo; + +public class MockPKIAuthenticationPlugin extends PKIAuthenticationPlugin { + SolrRequestInfo solrRequestInfo; + + Map remoteKeys = new HashMap<>(); + + public MockPKIAuthenticationPlugin(CoreContainer cores, String node) { + super(cores, node); + } + + @Override + boolean disabled() { + return false; + } + + @Override + SolrRequestInfo getRequestInfo() { + return solrRequestInfo; + } + + @Override + PublicKey getRemotePublicKey(String nodename) { + return remoteKeys.get(nodename); + } + + @Override + boolean isSolrThread() { + return true; + } +} diff --git a/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java b/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java index a752c60df88c..08a54b096039 100644 --- a/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java +++ b/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java @@ -21,8 +21,6 @@ import javax.servlet.http.HttpServletRequest; import java.security.Principal; import java.security.PublicKey; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import org.apache.http.Header; @@ -30,7 +28,6 @@ import org.apache.http.message.BasicHttpRequest; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.core.CoreContainer; import org.apache.solr.request.LocalSolrQueryRequest; import org.apache.solr.request.SolrRequestInfo; import org.apache.solr.response.SolrQueryResponse; @@ -43,36 +40,10 @@ public class TestPKIAuthenticationPlugin extends SolrTestCaseJ4 { - static class MockPKIAuthenticationPlugin extends PKIAuthenticationPlugin { - SolrRequestInfo solrRequestInfo; - - Map remoteKeys = new HashMap<>(); - - public MockPKIAuthenticationPlugin(CoreContainer cores, String node) { - super(cores, node); - } - - @Override - boolean disabled() { - return false; - } - - @Override - SolrRequestInfo getRequestInfo() { - return solrRequestInfo; - } - - @Override - PublicKey getRemotePublicKey(String nodename) { - return remoteKeys.get(nodename); - } - - @Override - boolean isSolrThread() { - return true; - } + public TestPKIAuthenticationPlugin() { } + @Test public void test() throws Exception { assumeWorkingMockito(); From 7609d8f761df2ba58a527ef33a4eb71df1a45aa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Thu, 5 Apr 2018 10:05:04 +0200 Subject: [PATCH 21/26] Dont remove blank line endings --- solr/CHANGES.txt | 8 ++++---- .../core/src/java/org/apache/solr/core/CoreContainer.java | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 5485400c5ead..0ba789f44c39 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -90,7 +90,7 @@ Bug Fixes * SOLR-12108: Fixed the fallback behavior of [raw] and [xml] transformers when an incompatble 'wt' was specified, the field value was lost if documentCache was not used. (hossman) -* SOLR-12087: Deleting replicas sometimes fails and causes the replicas to exist in the down +* SOLR-12087: Deleting replicas sometimes fails and causes the replicas to exist in the down state (Cao Manh Dat) * SOLR-11551: Standardize CoreAdmin API success/failure status codes (Jason Gerlowski, Steve Rowe) @@ -110,8 +110,8 @@ Bug Fixes * SOLR-12172: Fixed race condition that could cause an invalid set of collection properties to be kept in memory when multiple collection property changes are done in a short period of time. (Tomás Fernández Löbbe) - -* SOLR-11929: UpdateLog metrics are not initialized on core reload. (ab, Steve Rowe) + +* SOLR-11929: UpdateLog metrics are not initialized on core reload. (ab, Steve Rowe) Optimizations ---------------------- @@ -331,7 +331,7 @@ New Features * SOLR-12077: Add support for autoAddReplicas in the collection creation dialog in Admin UI. (shalin) -* SOLR-9510: introducing {!filters param=$fq excludeTags=f} query parser. +* SOLR-9510: introducing {!filters param=$fq excludeTags=f} query parser. Introducing {!.. filters=$fq excludeTags=t,q} in {!parent} and {!child} (Dr. Oleg Savrasov via Mikhail Khludnev) Bug Fixes diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java index 427e0f289a8a..28d9eec5ba6c 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -1107,7 +1107,7 @@ public boolean isSharedFs(CoreDescriptor cd) { private ConfigSet getConfigSet(CoreDescriptor cd) { return coreConfigService.getConfig(cd); } - + /** * Take action when we failed to create a SolrCore. If error is due to corrupt index, try to recover. Various recovery * strategies can be specified via system properties "-DCoreInitFailedAction={fromleader, none}" @@ -1385,7 +1385,7 @@ public void unload(String name) { public void unload(String name, boolean deleteIndexDir, boolean deleteDataDir, boolean deleteInstanceDir) { CoreDescriptor cd = solrCores.getCoreDescriptor(name); - + if (name != null) { // check for core-init errors first CoreLoadFailure loadFailure = coreInitFailures.remove(name); @@ -1402,7 +1402,7 @@ public void unload(String name, boolean deleteIndexDir, boolean deleteDataDir, b return; } } - + if (cd == null) { throw new SolrException(ErrorCode.BAD_REQUEST, "Cannot unload non-existent core [" + name + "]"); } @@ -1681,7 +1681,7 @@ public NodeConfig getNodeConfig() { public long getStatus() { return status; } - + // Occasaionally we need to access the transient cache handler in places other than coreContainer. public TransientSolrCoreCache getTransientCache() { return solrCores.getTransientCacheHandler(); From 5ca73277bda924a00d513b433129a2f23217140f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Fri, 6 Apr 2018 11:16:43 +0200 Subject: [PATCH 22/26] Get rid of nullpointer by pulling host, port, context from sys properties Initialize these properties where needed --- .../java/org/apache/solr/core/CoreContainer.java | 8 +++++--- .../solr/security/PKIAuthenticationPlugin.java | 2 ++ .../org/apache/solr/servlet/SolrDispatchFilter.java | 6 +++--- .../solr/security/TestPKIAuthenticationPlugin.java | 1 + .../src/java/org/apache/solr/SolrTestCaseJ4.java | 13 ++++++++++++- .../src/java/org/apache/solr/util/RestTestBase.java | 1 + 6 files changed, 24 insertions(+), 7 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java index 28d9eec5ba6c..206f324141bf 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -703,10 +703,12 @@ public void load() { } - // Builds a node name to be used with PKIAuth. - // Could use SysProps "host" and "jetty.port" directly, but we got those in cloudConfig already... + // Builds a node name to be used with PKIAuth when running in standalone mode. private String getNodeNameLocal() { - return getConfig().getCloudConfig().getHost()+":"+getConfig().getCloudConfig().getSolrHostPort()+"_solr"; + String host = System.getProperty("host"); + String port = System.getProperty("jetty.port"); + String context = System.getProperty("hostContext","solr"); + return String.format("%s:%s_%s", host, port, context.replace("^/", "").replace("/", "%2F")); } public void securityNodeChanged() { diff --git a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java index 247862ea8a4d..5264921b3944 100644 --- a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java +++ b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java @@ -81,6 +81,8 @@ public boolean isInterceptorRegistered(){ public PKIAuthenticationPlugin(CoreContainer cores, String nodeName) { this.cores = cores; myNodeName = nodeName; + // NOCOMMIT: DEBUGGING + log.info("PKI initialized with nodeName={}, zk={}", nodeName, cores.isZooKeeperAware()); } @Override diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java index d5b9347bc611..e97afedca27a 100644 --- a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java +++ b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java @@ -453,15 +453,15 @@ private boolean authenticateRequest(HttpServletRequest request, HttpServletRespo if (authenticationPlugin == null) { return true; } else { - String requestUri = ((HttpServletRequest) request).getRequestURI(); + String requestUri = request.getRequestURI(); log.info("authenticateRequest uri={}", requestUri); if (requestUri != null && requestUri.endsWith(PKIAuthenticationPlugin.PATH)) { - log.info("/info/key passthrogh"); + log.debug("/info/key passthrogh"); return true; // Let /admin/info/key through } else { log.info("/info/key FAILED requestUri.endsWith(PKIAuthenticationPlugin.PATH)={}", requestUri.endsWith(PKIAuthenticationPlugin.PATH)); } - String header = ((HttpServletRequest) request).getHeader(PKIAuthenticationPlugin.HEADER); + String header = request.getHeader(PKIAuthenticationPlugin.HEADER); if (header != null && cores.getPkiAuthenticationPlugin() != null) authenticationPlugin = cores.getPkiAuthenticationPlugin(); try { diff --git a/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java b/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java index 08a54b096039..a41f59d77893 100644 --- a/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java +++ b/solr/core/src/test/org/apache/solr/security/TestPKIAuthenticationPlugin.java @@ -128,6 +128,7 @@ PublicKey getRemotePublicKey(String nodename) { public void testGetBaseUrlForNodeNameLocal() { final MockPKIAuthenticationPlugin mock = new MockPKIAuthenticationPlugin(null, "myName"); assertEquals("http://my.host:9876/solr2", mock.getBaseUrlForNodeNameLocal("my.host:9876_solr2")); + assertEquals("http://my.host:9876/solr2/a", mock.getBaseUrlForNodeNameLocal("my.host:9876_solr2%2Fa")); System.setProperty("solr.jetty.keystore", "foo"); assertEquals("https://my.host:9876/solr2", mock.getBaseUrlForNodeNameLocal("my.host:9876_solr2")); System.clearProperty("solr.jetty.keystore"); diff --git a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java index 9fec7e64d610..82e80bb9a2b0 100644 --- a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java +++ b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java @@ -320,6 +320,8 @@ public static void teardownTestCases() throws Exception { System.clearProperty("urlScheme"); System.clearProperty("solr.peerSync.useRangeVersions"); System.clearProperty("solr.cloud.wait-for-updates-with-stale-state-pause"); + System.clearProperty("host"); + System.clearProperty("jetty.port"); HttpClientUtil.resetHttpClientBuilder(); clearNumericTypesProperties(); @@ -828,7 +830,16 @@ protected static int getNextAvailablePort() throws Exception { } return port; } - + + protected static void setHostPortContextFromUrl(URL url) { + System.setProperty("host", url.getHost()); + System.setProperty("jetty.port", String.valueOf(url.getPort())); + if (System.getProperty("hostContext") == null) + System.setProperty("hostContext", url.getFile().replace("^/", "")); + // NOCOMMIT: DEBUG print + log.info("Set system properties for host:{}, jetty.port:{}, hostContext:{}", + System.getProperty("host"), System.getProperty("jetty.port"), System.getProperty("hostContext")); + } /** Validates an update XML String is successful */ diff --git a/solr/test-framework/src/java/org/apache/solr/util/RestTestBase.java b/solr/test-framework/src/java/org/apache/solr/util/RestTestBase.java index 12cad01f6a12..8cf28933a6de 100644 --- a/solr/test-framework/src/java/org/apache/solr/util/RestTestBase.java +++ b/solr/test-framework/src/java/org/apache/solr/util/RestTestBase.java @@ -52,6 +52,7 @@ public static void cleanUpHarness() throws IOException { createJetty(solrHome, configFile, schemaFile, context, stopAtShutdown, extraServlets); restTestHarness = new RestTestHarness(() -> jetty.getBaseUrl().toString() + "/" + DEFAULT_TEST_CORENAME); + setHostPortContextFromUrl(jetty.getBaseUrl()); } /** Validates an update XML String is successful From 147cb07d77494116f02d37574cda295432188e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Fri, 6 Apr 2018 12:21:33 +0200 Subject: [PATCH 23/26] Remove custom basic auth test methods in TestCase --- .../solr/BaseDistributedSearchTestCase.java | 30 +++---------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java b/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java index c22f30615826..0d9951eb8832 100644 --- a/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java +++ b/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java @@ -598,31 +598,18 @@ protected QueryResponse query(boolean setDistribParams, Object[] q) throws Excep * Returns the QueryResponse from {@link #queryServer} */ protected QueryResponse query(boolean setDistribParams, SolrParams p) throws Exception { - return query(setDistribParams, p, null, null); - } - - /** - * Returns the QueryResponse from {@link #queryServer} - * @param setDistribParams whether to do a distributed request - * @param user basic auth username (set to null if not in use) - * @param pass basic auth password (set to null if not in use) - * @return the query response - */ - protected QueryResponse query(boolean setDistribParams, SolrParams p, String user, String pass) throws Exception { final ModifiableSolrParams params = new ModifiableSolrParams(p); // TODO: look into why passing true causes fails params.set("distrib", "false"); - QueryRequest req = generateQueryRequest(params, user, pass); - final QueryResponse controlRsp = req.process(controlClient, null); + final QueryResponse controlRsp = controlClient.query(params); validateControlData(controlRsp); params.remove("distrib"); if (setDistribParams) setDistributedParams(params); - req = generateQueryRequest(params, user, pass); - QueryResponse rsp = queryServer(req); + QueryResponse rsp = queryServer(params); compareResponses(rsp, controlRsp); @@ -637,8 +624,7 @@ public void run() { int which = r.nextInt(clients.size()); SolrClient client = clients.get(which); try { - QueryRequest qreq = generateQueryRequest(new ModifiableSolrParams(params), user, pass); - QueryResponse rsp = qreq.process(client, null); + QueryResponse rsp = client.query(new ModifiableSolrParams(params)); if (verifyStress) { compareResponses(rsp, controlRsp); } @@ -657,15 +643,7 @@ public void run() { } return rsp; } - - private QueryRequest generateQueryRequest(ModifiableSolrParams params, String user, String pass) { - QueryRequest req = new QueryRequest(params); - if (user != null && pass != null) { - req.setBasicAuthCredentials(user, pass); - } - return req; - } - + public QueryResponse queryAndCompare(SolrParams params, SolrClient... clients) throws SolrServerException, IOException { return queryAndCompare(params, Arrays.asList(clients)); } From 9dde627f24fc144b4a838b354c446c102e6ec3a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Sun, 8 Apr 2018 02:20:50 +0200 Subject: [PATCH 24/26] Change test to work with distrib --- .../org/apache/solr/security/BasicAuthDistributedTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java index e35e369dac8c..560d962d2151 100644 --- a/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java +++ b/solr/core/src/test/org/apache/solr/security/BasicAuthDistributedTest.java @@ -100,9 +100,9 @@ private void testAuth() throws Exception { rsp = req.process(clients.get(0), null); if (jettys.size() > 1) { assertTrue(rsp.getResults().getNumFound() < 10); - rsp = query(true, params, "solr", "SolrRocks"); + } else { + assertEquals(10, rsp.getResults().getNumFound()); } - assertEquals(10, rsp.getResults().getNumFound()); // Disable auth for (JettySolrRunner j : jettys) { From c76b830cf2f4feb43288749da4bbb4a9e60c94da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Sun, 8 Apr 2018 22:05:00 +0200 Subject: [PATCH 25/26] Remove nocommit --- .../java/org/apache/solr/security/PKIAuthenticationPlugin.java | 2 -- .../src/java/org/apache/solr/SolrTestCaseJ4.java | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java index 5264921b3944..247862ea8a4d 100644 --- a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java +++ b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java @@ -81,8 +81,6 @@ public boolean isInterceptorRegistered(){ public PKIAuthenticationPlugin(CoreContainer cores, String nodeName) { this.cores = cores; myNodeName = nodeName; - // NOCOMMIT: DEBUGGING - log.info("PKI initialized with nodeName={}, zk={}", nodeName, cores.isZooKeeperAware()); } @Override diff --git a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java index 82e80bb9a2b0..16292c48c5aa 100644 --- a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java +++ b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java @@ -836,8 +836,7 @@ protected static void setHostPortContextFromUrl(URL url) { System.setProperty("jetty.port", String.valueOf(url.getPort())); if (System.getProperty("hostContext") == null) System.setProperty("hostContext", url.getFile().replace("^/", "")); - // NOCOMMIT: DEBUG print - log.info("Set system properties for host:{}, jetty.port:{}, hostContext:{}", + log.debug("Set system properties for host:{}, jetty.port:{}, hostContext:{}", System.getProperty("host"), System.getProperty("jetty.port"), System.getProperty("hostContext")); } From a9c51ade1be094c6dbc56767acd0c1b48583c945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Mon, 9 Apr 2018 10:43:56 +0200 Subject: [PATCH 26/26] Fix precommit fail forbidden api --- solr/core/src/java/org/apache/solr/core/CoreContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java index 206f324141bf..f01a5c8569e4 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -708,7 +708,7 @@ private String getNodeNameLocal() { String host = System.getProperty("host"); String port = System.getProperty("jetty.port"); String context = System.getProperty("hostContext","solr"); - return String.format("%s:%s_%s", host, port, context.replace("^/", "").replace("/", "%2F")); + return String.format(Locale.ROOT, "%s:%s_%s", host, port, context.replace("^/", "").replace("/", "%2F")); } public void securityNodeChanged() {