blob: dd5b52f2ab176737d9f569a4309e10b522a8bb19 [file] [log] [blame]
/*
* 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 {
@SuppressWarnings({"unchecked", "rawtypes"})
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());
try (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);
@SuppressWarnings({"rawtypes"})
List l = (List) ((Map) ((List)rsp.getValues().get("errorMessages")).get(0)).get("errorMessages");
assertEquals(1, l.size());
handler.close();
}
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{
try (MockSecurityHandler msh = new MockSecurityHandler()) {
System.out.println(msh.getStandardJson());
}
}
}