/* * 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.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.common.util.Utils; import org.apache.solr.request.LocalSolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.security.BasicAuthPlugin; import org.apache.solr.security.RuleBasedAuthorizationPlugin; import org.apache.solr.common.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 { public void testEdit() throws Exception { MockSecurityHandler handler = new MockSecurityHandler(); String command = "{\n" + "'set-user': {'tom':'TomIsCool'},\n" + "'set-user':{ 'tom':'TomIsUberCool'}\n" + "}"; LocalSolrQueryRequest req = new LocalSolrQueryRequest(null, new ModifiableSolrParams()); req.getContext().put("httpMethod","POST"); req.getContext().put("path","/admin/authentication"); ContentStreamBase.ByteArrayStream o = new ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8),""); req.setContentStreams(Collections.singletonList(o)); handler.handleRequestBody(req,new SolrQueryResponse()); BasicAuthPlugin basicAuth = new BasicAuthPlugin(); SecurityConfig securityCfg = handler.m.get("/security.json"); basicAuth.init((Map<String, Object>) securityCfg.getData().get("authentication")); assertTrue(basicAuth.authenticate("tom", "TomIsUberCool")); command = "{\n" + "'set-user': {'harry':'HarryIsCool'},\n" + "'delete-user': ['tom','harry']\n" + "}"; o = new ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8),""); req.setContentStreams(Collections.singletonList(o)); handler.handleRequestBody(req,new SolrQueryResponse()); 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()); command = "{'set-permission':{ collection : acoll ,\n" + " path : '/nonexistentpath',\n" + " role :guest },\n" + "'set-user-role': { 'tom': ['admin','dev']},"+ "'set-permission':{'name': 'security-edit',\n" + " 'role': 'admin'}\n" + "}"; req = new LocalSolrQueryRequest(null, new ModifiableSolrParams()); req.getContext().put("httpMethod","POST"); req.getContext().put("path","/admin/authorization"); o = new ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8),""); req.setContentStreams(Collections.singletonList(o)); SolrQueryResponse rsp = new SolrQueryResponse(); handler.handleRequestBody(req, rsp); assertNull(rsp.getValues().get(CommandOperation.ERR_MSGS)); 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")); assertTrue(tomRoles.contains("dev")); List<Map> permissions = (List<Map>) authzconf.get("permissions"); assertEquals(2, permissions.size()); for (Map p : permissions) { assertEquals("acoll", p.get("collection")); break; } command = "{\n" + "'set-permission':{index : 2, name : security-edit,\n" + " 'role': ['admin','dev']\n" + " }}"; req = new LocalSolrQueryRequest(null, new ModifiableSolrParams()); req.getContext().put("httpMethod","POST"); req.getContext().put("path","/admin/authorization"); o = new ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8),""); req.setContentStreams(Collections.singletonList(o)); rsp = new SolrQueryResponse(); handler.handleRequestBody(req, rsp); authzconf = (Map) handler.m.get("/security.json").getData().get("authorization"); permissions = (List<Map>) authzconf.get("permissions"); Map p = permissions.get(1); assertEquals("security-edit", p.get("name")); List rol = (List) p.get("role"); assertEquals( "admin", rol.get(0)); assertEquals( "dev", rol.get(1)); command = "{\n" + "'update-permission':{'index': 1,\n" + " 'role': ['guest','admin']\n" + " }}"; req = new LocalSolrQueryRequest(null, new ModifiableSolrParams()); req.getContext().put("httpMethod","POST"); req.getContext().put("path","/admin/authorization"); o = new ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8),""); req.setContentStreams(Collections.singletonList(o)); rsp = new SolrQueryResponse(); handler.handleRequestBody(req, rsp); authzconf = (Map) handler.m.get("/security.json").getData().get("authorization"); permissions = (List<Map>) authzconf.get("permissions"); p = permissions.get(0); assertEquals("acoll", p.get("collection")); rol = (List) p.get("role"); assertEquals( "guest", rol.get(0)); assertEquals( "admin", rol.get(1)); command = "{\n" + "delete-permission: 1,\n" + " set-user-role : { tom :null}\n" + "}"; req = new LocalSolrQueryRequest(null, new ModifiableSolrParams()); req.getContext().put("httpMethod","POST"); req.getContext().put("path","/admin/authorization"); o = new ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8),""); req.setContentStreams(Collections.singletonList(o)); rsp = new SolrQueryResponse(); handler.handleRequestBody(req, rsp); assertNull(rsp.getValues().get(CommandOperation.ERR_MSGS)); authzconf = (Map) handler.m.get("/security.json").getData().get("authorization"); userRoles = (Map) authzconf.get("user-role"); assertEquals(0, userRoles.size()); permissions = (List<Map>) authzconf.get("permissions"); assertEquals(1, permissions.size()); for (Map permission : permissions) { assertFalse("some-permission".equals(permission.get("name"))); } command = "{\n" + "'set-permission':{index : 2, 'name': 'security-edit',\n" + " 'method':'POST',"+ // -ve test security edit is a well-known permission , only role attribute should be provided " 'role': 'admin'\n" + " }}"; req = new LocalSolrQueryRequest(null, new ModifiableSolrParams()); req.getContext().put("httpMethod","POST"); req.getContext().put("path","/admin/authorization"); o = new ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8),""); req.setContentStreams(Collections.singletonList(o)); rsp = new SolrQueryResponse(); handler.handleRequestBody(req, rsp); List l = (List) ((Map) ((List)rsp.getValues().get("errorMessages")).get(0)).get("errorMessages"); assertEquals(1, l.size()); } public static class MockSecurityHandler extends SecurityConfHandler { private Map<String, SecurityConfig> m; final BasicAuthPlugin basicAuthPlugin = new BasicAuthPlugin(); final RuleBasedAuthorizationPlugin rulesBasedAuthorizationPlugin = new RuleBasedAuthorizationPlugin(); public MockSecurityHandler() { super(null); m = new HashMap<>(); 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<String, SecurityConfig> getM() { return m; } @Override Object getPlugin(String key) { if (key.equals("authentication")) { return basicAuthPlugin; } if (key.equals("authorization")) { return rulesBasedAuthorizationPlugin; } return null; } @Override protected void getConf(SolrQueryResponse rsp, String key) { // NOP } @Override public SecurityConfig getSecurityConfig(boolean getFresh) { return m.get("/security.json"); } @Override 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; } } public String getStandardJson() throws Exception { String command = "{\n" + "'set-user': {'solr':'SolrRocks'}\n" + "}"; LocalSolrQueryRequest req = new LocalSolrQueryRequest(null, new ModifiableSolrParams()); req.getContext().put("httpMethod","POST"); req.getContext().put("path","/admin/authentication"); ContentStreamBase.ByteArrayStream o = new ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8),""); req.setContentStreams(Collections.singletonList(o)); handleRequestBody(req, new SolrQueryResponse()); command = "{'set-user-role': { 'solr': 'admin'},\n" + "'set-permission':{'name': 'security-edit', 'role': 'admin'}" + "}"; req = new LocalSolrQueryRequest(null, new ModifiableSolrParams()); req.getContext().put("httpMethod","POST"); req.getContext().put("path","/admin/authorization"); o = new ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8),""); req.setContentStreams(Collections.singletonList(o)); SolrQueryResponse rsp = new SolrQueryResponse(); handleRequestBody(req, rsp); Map<String, Object> data = m.get("/security.json").getData(); ((Map)data.get("authentication")).remove(""); ((Map)data.get("authorization")).remove(""); return Utils.toJSONString (data); } } public static void main(String[] args) throws Exception{ System.out.println(new MockSecurityHandler().getStandardJson()); } }