diff --git a/solr/core/src/java/org/apache/solr/security/AuthenticationPlugin.java b/solr/core/src/java/org/apache/solr/security/AuthenticationPlugin.java index fe356c77258..0f4442c1621 100644 --- a/solr/core/src/java/org/apache/solr/security/AuthenticationPlugin.java +++ b/solr/core/src/java/org/apache/solr/security/AuthenticationPlugin.java @@ -96,11 +96,20 @@ public abstract class AuthenticationPlugin implements SolrInfoBean { } HttpServletRequest wrapWithPrincipal(HttpServletRequest request, Principal principal) { + return wrapWithPrincipal(request, principal, principal.getName()); + } + + HttpServletRequest wrapWithPrincipal(HttpServletRequest request, Principal principal, String username) { return new HttpServletRequestWrapper(request) { @Override public Principal getUserPrincipal() { return principal; } + + @Override + public String getRemoteUser() { + return username; + } }; } diff --git a/solr/core/src/java/org/apache/solr/security/AuthorizationContext.java b/solr/core/src/java/org/apache/solr/security/AuthorizationContext.java index a3545dca245..ef8f6c9cfc3 100644 --- a/solr/core/src/java/org/apache/solr/security/AuthorizationContext.java +++ b/solr/core/src/java/org/apache/solr/security/AuthorizationContext.java @@ -41,13 +41,35 @@ public abstract class AuthorizationContext { } public abstract SolrParams getParams() ; - + + + /** + * This method returns the {@link Principal} corresponding to + * the authenticated user for the current request. The value returned by + * {@link Principal#getName()} depends on the authentication mechanism + * used (e.g. for user "foo" with BASIC authentication the result would be + * "foo". On the other hand with KERBEROS it would be foo@REALMNAME). + * The {@link #getUserName()} method may be preferred to extract the identity + * of the authenticated user instead of this method. + * + * @return user principal in case of an authenticated request + * null in case of unauthenticated request + */ public abstract Principal getUserPrincipal() ; + /** + * This method returns the name of the authenticated user for the current request. + * The return value of this method is agnostic of the underlying authentication + * mechanism used. + * + * @return user name in case of an authenticated user + * null in case of unauthenticated request + */ + public abstract String getUserName(); + public abstract String getHttpHeader(String header); - @SuppressWarnings({"rawtypes"}) - public abstract Enumeration getHeaderNames(); + public abstract Enumeration getHeaderNames(); public abstract String getRemoteAddr(); 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 82405bb0fc7..6bc35bf5221 100644 --- a/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java +++ b/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java @@ -145,7 +145,7 @@ public class BasicAuthPlugin extends AuthenticationPlugin implements ConfigEdita return false; } else { Principal principal = new BasicAuthUserPrincipal(username, pwd); - request = wrapWithPrincipal(request, principal); + request = wrapWithPrincipal(request, principal, username); numAuthenticated.inc(); filterChain.doFilter(request, response); return true; diff --git a/solr/core/src/java/org/apache/solr/security/DelegationTokenKerberosFilter.java b/solr/core/src/java/org/apache/solr/security/DelegationTokenKerberosFilter.java index 0184564d683..68a97a1558b 100644 --- a/solr/core/src/java/org/apache/solr/security/DelegationTokenKerberosFilter.java +++ b/solr/core/src/java/org/apache/solr/security/DelegationTokenKerberosFilter.java @@ -17,7 +17,6 @@ package org.apache.solr.security; import java.io.IOException; -import java.lang.invoke.MethodHandles; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; @@ -49,8 +48,6 @@ import org.apache.solr.common.cloud.ZkCredentialsProvider; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.ACL; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * This is an authentication filter based on Hadoop's {@link DelegationTokenAuthenticationFilter}. @@ -58,8 +55,6 @@ import org.slf4j.LoggerFactory; * application to reuse the authentication of an end-user or another application. */ public class DelegationTokenKerberosFilter extends DelegationTokenAuthenticationFilter { - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - private CuratorFramework curatorFramework; private final Locale defaultLocale = Locale.getDefault(); @@ -83,13 +78,12 @@ public class DelegationTokenKerberosFilter extends DelegationTokenAuthentication * "solr.impersonator.user.name" will be added to the configuration. */ @Override - protected Configuration getProxyuserConfiguration(FilterConfig filterConf) - throws ServletException { + protected Configuration getProxyuserConfiguration(FilterConfig filterConf) { Configuration conf = new Configuration(false); - Enumeration names = filterConf.getInitParameterNames(); + Enumeration names = filterConf.getInitParameterNames(); while (names.hasMoreElements()) { - String name = (String) names.nextElement(); + String name = names.nextElement(); if (name.startsWith(KerberosPlugin.IMPERSONATOR_PREFIX)) { String value = filterConf.getInitParameter(name); conf.set(PROXYUSER_PREFIX + "." + name.substring(KerberosPlugin.IMPERSONATOR_PREFIX.length()), value); @@ -163,11 +157,8 @@ public class DelegationTokenKerberosFilter extends DelegationTokenAuthentication // without the appropriate ACL configuration. This issue is possibly related to HADOOP-11973 try { zkClient.makePath(SecurityAwareZkACLProvider.SECURITY_ZNODE_PATH, CreateMode.PERSISTENT, true); - - } catch (KeeperException ex) { - if (ex.code() != KeeperException.Code.NODEEXISTS) { - throw ex; - } + } catch (KeeperException.NodeExistsException ex) { + // ignore? } curatorFramework = CuratorFrameworkFactory.builder() diff --git a/solr/core/src/java/org/apache/solr/security/ExternalRoleRuleBasedAuthorizationPlugin.java b/solr/core/src/java/org/apache/solr/security/ExternalRoleRuleBasedAuthorizationPlugin.java index 7575c167d9c..a57a923a8fe 100644 --- a/solr/core/src/java/org/apache/solr/security/ExternalRoleRuleBasedAuthorizationPlugin.java +++ b/solr/core/src/java/org/apache/solr/security/ExternalRoleRuleBasedAuthorizationPlugin.java @@ -16,24 +16,19 @@ */ package org.apache.solr.security; -import java.lang.invoke.MethodHandles; import java.security.Principal; import java.util.Collections; import java.util.Map; import java.util.Set; import org.apache.solr.common.SolrException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Rule Based Authz plugin implementation which reads user roles from the request. This requires * a Principal implementing VerifiedUserRoles interface, e.g. JWTAuthenticationPlugin */ public class ExternalRoleRuleBasedAuthorizationPlugin extends RuleBasedAuthorizationPluginBase { - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - @Override + @Override public void init(Map initInfo) { super.init(initInfo); if (initInfo.containsKey("user-role")) { diff --git a/solr/core/src/java/org/apache/solr/security/KerberosPlugin.java b/solr/core/src/java/org/apache/solr/security/KerberosPlugin.java index 7877643b43a..42d64911fe2 100644 --- a/solr/core/src/java/org/apache/solr/security/KerberosPlugin.java +++ b/solr/core/src/java/org/apache/solr/security/KerberosPlugin.java @@ -114,9 +114,7 @@ public class KerberosPlugin extends AuthenticationPlugin implements HttpClientBu putParamOptional(params, "kerberos.keytab", KEYTAB_PARAM); } - String delegationTokenStr = System.getProperty(DELEGATION_TOKEN_ENABLED, null); - boolean delegationTokenEnabled = - (delegationTokenStr == null) ? false : Boolean.parseBoolean(delegationTokenStr); + boolean delegationTokenEnabled = Boolean.getBoolean(DELEGATION_TOKEN_ENABLED); ZkController controller = coreContainer.getZkController(); if (delegationTokenEnabled) { @@ -162,7 +160,7 @@ public class KerberosPlugin extends AuthenticationPlugin implements HttpClientBu } // check impersonator config - for (@SuppressWarnings({"rawtypes"})Enumeration e = System.getProperties().propertyNames(); e.hasMoreElements();) { + for (Enumeration e = System.getProperties().propertyNames(); e.hasMoreElements();) { String key = e.nextElement().toString(); if (key.startsWith(IMPERSONATOR_PREFIX)) { if (!delegationTokenEnabled) { diff --git a/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPlugin.java b/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPlugin.java index 78af04010b4..a4e8ded369c 100644 --- a/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPlugin.java +++ b/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPlugin.java @@ -16,15 +16,11 @@ */ package org.apache.solr.security; -import java.lang.invoke.MethodHandles; import java.security.Principal; import java.util.HashMap; import java.util.Map; import java.util.Set; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import static org.apache.solr.handler.admin.SecurityConfHandler.getMapValue; /** @@ -32,24 +28,33 @@ import static org.apache.solr.handler.admin.SecurityConfHandler.getMapValue; * mapping in the security.json configuration */ public class RuleBasedAuthorizationPlugin extends RuleBasedAuthorizationPluginBase { - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - private final Map> usersVsRoles = new HashMap<>(); + private boolean useShortName; @Override public void init(Map initInfo) { super.init(initInfo); Map map = getMapValue(initInfo, "user-role"); for (Object o : map.entrySet()) { - @SuppressWarnings({"rawtypes"}) - Map.Entry e = (Map.Entry) o; - String roleName = (String) e.getKey(); + @SuppressWarnings("unchecked") + Map.Entry e = (Map.Entry) o; + String roleName = e.getKey(); usersVsRoles.put(roleName, Permission.readValueAsSet(map, roleName)); } + useShortName = Boolean.parseBoolean(initInfo.getOrDefault("useShortName", Boolean.FALSE).toString()); + } + + @Override + public Set getUserRoles(AuthorizationContext context) { + if (useShortName) { + return usersVsRoles.get(context.getUserName()); + } else { + return getUserRoles(context.getUserPrincipal()); + } } /** - * Look up user's role from the explicit user-role mapping + * Look up user's role from the explicit user-role mapping. * * @param principal the user Principal from the request * @return set of roles as strings diff --git a/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPluginBase.java b/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPluginBase.java index 8a485f7b245..e1da856c99a 100644 --- a/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPluginBase.java +++ b/solr/core/src/java/org/apache/solr/security/RuleBasedAuthorizationPluginBase.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.lang.invoke.MethodHandles; import java.security.Principal; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -34,8 +35,6 @@ import org.apache.solr.common.util.CommandOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static java.util.Arrays.asList; -import static java.util.Collections.unmodifiableMap; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; import static org.apache.solr.handler.admin.SecurityConfHandler.getListValue; @@ -47,38 +46,40 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private final Map mapping = new HashMap<>(); - private final List permissions = new ArrayList<>(); - - private static class WildCardSupportMap extends HashMap> { + // Doesn't implement Map because we violate the contracts of put() and get() + private static class WildCardSupportMap { final Set wildcardPrefixes = new HashSet<>(); + final Map> delegate = new HashMap<>(); - @Override public List put(String key, List value) { if (key != null && key.endsWith("/*")) { key = key.substring(0, key.length() - 2); wildcardPrefixes.add(key); } - return super.put(key, value); + return delegate.put(key, value); } - @Override - public List get(Object key) { - List result = super.get(key); + public List get(String key) { + List result = delegate.get(key); if (key == null || result != null) return result; - if (!wildcardPrefixes.isEmpty()) { - for (String s : wildcardPrefixes) { - if (key.toString().startsWith(s)) { - List l = super.get(s); - if (l != null) { - result = result == null ? new ArrayList<>() : new ArrayList<>(result); - result.addAll(l); - } + + for (String s : wildcardPrefixes) { + if (key.startsWith(s)) { + List wildcardPermissions = delegate.get(s); + if (wildcardPermissions != null) { + if (result == null) result = new ArrayList<>(); + result.addAll(wildcardPermissions); } } } + return result; } + + public Set keySet() { + return delegate.keySet(); + } } @Override @@ -109,8 +110,7 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP return flag.rsp; } - private MatchStatus checkCollPerm(Map> pathVsPerms, - AuthorizationContext context) { + private MatchStatus checkCollPerm(WildCardSupportMap pathVsPerms, AuthorizationContext context) { if (pathVsPerms == null) return MatchStatus.NO_PERMISSIONS_FOUND; if (log.isTraceEnabled()) { @@ -131,7 +131,6 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP if (permissions == null || permissions.isEmpty()) { return MatchStatus.NO_PERMISSIONS_FOUND; } - Principal principal = context.getUserPrincipal(); log.trace("Following perms are associated with this collection and path: [{}]", permissions); final Permission governingPermission = findFirstGoverningPermission(permissions, context); @@ -145,7 +144,7 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP log.debug("Found perm [{}] to govern resource [{}]", governingPermission, context.getResource()); } - return determineIfPermissionPermitsPrincipal(principal, governingPermission); + return determineIfPermissionPermitsPrincipal(context, governingPermission); } private Permission findFirstGoverningPermission(List permissions, AuthorizationContext context) { @@ -216,11 +215,12 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP return true; } - private MatchStatus determineIfPermissionPermitsPrincipal(Principal principal, Permission governingPermission) { + private MatchStatus determineIfPermissionPermitsPrincipal(AuthorizationContext context, Permission governingPermission) { if (governingPermission.role == null) { log.debug("Governing permission [{}] has no role; permitting access", governingPermission); return MatchStatus.PERMITTED; } + Principal principal = context.getUserPrincipal(); if (principal == null) { log.debug("Governing permission [{}] has role, but request principal cannot be identified; forbidding access", governingPermission); return MatchStatus.USER_REQUIRED; @@ -229,7 +229,7 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP return MatchStatus.PERMITTED; } - Set userRoles = getUserRoles(principal); + Set userRoles = getUserRoles(context); for (String role : governingPermission.role) { if (userRoles != null && userRoles.contains(role)) { log.debug("Governing permission [{}] allows access to role [{}]; permitting access", governingPermission, role); @@ -260,7 +260,7 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP @Override @SuppressWarnings({"unchecked"}) - public void init(@SuppressWarnings({"rawtypes"})Map initInfo) { + public void init(Map initInfo) { mapping.put(null, new WildCardSupportMap()); @SuppressWarnings({"rawtypes"}) List perms = getListValue(initInfo, "permissions"); @@ -272,7 +272,6 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP log.error("Invalid permission ", exp); continue; } - permissions.add(p); add2Mapping(p); } } @@ -280,8 +279,7 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP //this is to do optimized lookup of permissions for a given collection/path private void add2Mapping(Permission permission) { for (String c : permission.collections) { - WildCardSupportMap m = mapping.get(c); - if (m == null) mapping.put(c, m = new WildCardSupportMap()); + WildCardSupportMap m = mapping.computeIfAbsent(c, k -> new WildCardSupportMap()); for (String path : permission.path) { List perms = m.get(path); if (perms == null) m.put(path, perms = new ArrayList<>()); @@ -290,6 +288,15 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP } } + /** + * Finds user roles + * @param context the authorization context to load roles from + * @return set of roles as strings or empty set if no roles are found + */ + public Set getUserRoles(AuthorizationContext context) { + return getUserRoles(context.getUserPrincipal()); + } + /** * Finds users roles * @param principal the user Principal to fetch roles for @@ -330,12 +337,11 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP return latestConf; } - private static final Map ops = unmodifiableMap(asList(AutorizationEditOperation.values()).stream().collect(toMap(AutorizationEditOperation::getOperationName, identity()))); + private static final Map ops = Arrays.stream(AutorizationEditOperation.values()).collect(toMap(AutorizationEditOperation::getOperationName, identity())); @Override public ValidatingJsonMap getSpec() { return Utils.getSpec("cluster.security.RuleBasedAuthorization").getSpec(); - } } 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 9b95fd59946..2b64cc088e4 100644 --- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java +++ b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java @@ -1133,6 +1133,11 @@ public class HttpSolrCall { return getReq().getUserPrincipal(); } + @Override + public String getUserName() { + return getReq().getRemoteUser(); + } + @Override public String getHttpHeader(String s) { return getReq().getHeader(s); diff --git a/solr/core/src/test-files/solr/security/hadoop_kerberos_authz_config.json b/solr/core/src/test-files/solr/security/hadoop_kerberos_authz_config.json new file mode 100644 index 00000000000..92081b77af4 --- /dev/null +++ b/solr/core/src/test-files/solr/security/hadoop_kerberos_authz_config.json @@ -0,0 +1,37 @@ +{ + "authentication": { + "class": "org.apache.solr.security.ConfigurableInternodeAuthHadoopPlugin", + "sysPropPrefix": "solr.", + "type": "kerberos", + "clientBuilderFactory": "org.apache.solr.client.solrj.impl.Krb5HttpClientBuilder", + "initKerberosZk": "true", + "authConfigs": [ + "kerberos.principal", + "kerberos.keytab", + "kerberos.name.rules" + ], + "defaultConfigs": { + } + }, + "authorization":{ + "class":"solr.RuleBasedAuthorizationPlugin", + "useShortName": "true", + "permissions":[ + { + "name":"collection-admin-edit", + "role":"admin" + }, + { + "name":"read", + "role":"admin" + }, + { + "name":"update", + "role":"admin" + } + ], + "user-role":{ + "solr":"admin" + } + } +} diff --git a/solr/core/src/test/org/apache/solr/security/BaseTestRuleBasedAuthorizationPlugin.java b/solr/core/src/test/org/apache/solr/security/BaseTestRuleBasedAuthorizationPlugin.java index e52b09a695d..d975caa0075 100644 --- a/solr/core/src/test/org/apache/solr/security/BaseTestRuleBasedAuthorizationPlugin.java +++ b/solr/core/src/test/org/apache/solr/security/BaseTestRuleBasedAuthorizationPlugin.java @@ -23,10 +23,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import org.apache.http.auth.BasicUserPrincipal; import org.apache.solr.SolrTestCaseJ4; @@ -45,8 +43,10 @@ import org.apache.solr.handler.component.SearchHandler; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.security.AuthorizationContext.CollectionRequest; import org.apache.solr.security.AuthorizationContext.RequestType; +import org.apache.solr.util.LogLevel; import org.hamcrest.core.IsInstanceOf; import org.hamcrest.core.IsNot; +import org.junit.Before; import org.junit.Test; import static java.util.Collections.emptyMap; @@ -54,16 +54,18 @@ import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static org.apache.solr.common.util.CommandOperation.captureErrors; import static org.apache.solr.common.util.Utils.getObjectByPath; -import static org.apache.solr.common.util.Utils.makeMap; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assume.assumeThat; /** * Base class for testing RBAC. This will test the {@link RuleBasedAuthorizationPlugin} implementation * but also serves as a base class for testing other sub classes */ @SuppressWarnings("unchecked") +@LogLevel("org.apache.solr.security=TRACE") public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { - @SuppressWarnings({"rawtypes"}) - protected Map rules; + protected Map rules; final int STATUS_OK = 200; final int FORBIDDEN = 403; @@ -75,6 +77,11 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { resetPermissionsAndRoles(); } + @Before + public void setupPermissionsAndRoles() { + resetPermissionsAndRoles(); + } + protected void resetPermissionsAndRoles() { String permissions = "{" + " user-role : {" + @@ -97,110 +104,101 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { " }," + "{name:read, role:dev }," + "{name:freeforall, path:'/foo', role:'*'}]}"; - rules = (Map) Utils.fromJSONString(permissions); + rules = (Map) Utils.fromJSONString(permissions); } @Test public void testBasicPermissions() { - checkRules(makeMap("resource", "/update/json/docs", + checkRules(Map.of("resource", "/update/json/docs", "httpMethod", "POST", "userPrincipal", "unknownuser", "collectionRequests", "freeforall", "handler", new UpdateRequestHandler()) , STATUS_OK); - checkRules(makeMap("resource", "/update/json/docs", + checkRules(Map.of("resource", "/update/json/docs", "httpMethod", "POST", "userPrincipal", "tim", "collectionRequests", "mycoll", "handler", new UpdateRequestHandler()) , STATUS_OK); - checkRules(makeMap("resource", "/update/json/docs", + checkRules(Map.of("resource", "/update/json/docs", "httpMethod", "POST", "collectionRequests", "mycoll", "handler", new UpdateRequestHandler()) , PROMPT_FOR_CREDENTIALS); - checkRules(makeMap("resource", "/schema", + checkRules(Map.of("resource", "/schema", "userPrincipal", "somebody", "collectionRequests", "mycoll", "httpMethod", "POST", "handler", new SchemaHandler()) , FORBIDDEN); - checkRules(makeMap("resource", "/schema", + checkRules(Map.of("resource", "/schema", "userPrincipal", "somebody", "collectionRequests", "mycoll", "httpMethod", "GET", "handler", new SchemaHandler()) , STATUS_OK); - checkRules(makeMap("resource", "/schema/fields", + checkRules(Map.of("resource", "/schema/fields", "userPrincipal", "somebody", "collectionRequests", "mycoll", "httpMethod", "GET", "handler", new SchemaHandler()) , STATUS_OK); - checkRules(makeMap("resource", "/schema", + checkRules(Map.of("resource", "/schema", "userPrincipal", "somebody", "collectionRequests", "mycoll", "httpMethod", "POST", "handler", new SchemaHandler()) , FORBIDDEN); - checkRules(makeMap("resource", "/admin/collections", + checkRules(Map.of("resource", "/admin/collections", "userPrincipal", "tim", "requestType", RequestType.ADMIN, - "collectionRequests", null, "httpMethod", "GET", "handler", new CollectionsHandler(), "params", new MapSolrParams(singletonMap("action", "LIST"))) , STATUS_OK); - checkRules(makeMap("resource", "/admin/collections", - "userPrincipal", null, + checkRules(Map.of("resource", "/admin/collections", "requestType", RequestType.ADMIN, - "collectionRequests", null, "httpMethod", "GET", "handler", new CollectionsHandler(), "params", new MapSolrParams(singletonMap("action", "LIST"))) , STATUS_OK); - checkRules(makeMap("resource", "/admin/collections", - "userPrincipal", null, + checkRules(Map.of("resource", "/admin/collections", "requestType", RequestType.ADMIN, - "collectionRequests", null, "handler", new CollectionsHandler(), "params", new MapSolrParams(singletonMap("action", "CREATE"))) , PROMPT_FOR_CREDENTIALS); - checkRules(makeMap("resource", "/admin/collections", - "userPrincipal", null, + checkRules(Map.of("resource", "/admin/collections", "requestType", RequestType.ADMIN, - "collectionRequests", null, "handler", new CollectionsHandler(), "params", new MapSolrParams(singletonMap("action", "RELOAD"))) , PROMPT_FOR_CREDENTIALS); - checkRules(makeMap("resource", "/admin/collections", + checkRules(Map.of("resource", "/admin/collections", "userPrincipal", "somebody", "requestType", RequestType.ADMIN, - "collectionRequests", null, "handler", new CollectionsHandler(), "params", new MapSolrParams(singletonMap("action", "CREATE"))) , FORBIDDEN); - checkRules(makeMap("resource", "/admin/collections", + checkRules(Map.of("resource", "/admin/collections", "userPrincipal", "tim", "requestType", RequestType.ADMIN, - "collectionRequests", null, "handler", new CollectionsHandler(), "params", new MapSolrParams(singletonMap("action", "CREATE"))) , STATUS_OK); - checkRules(makeMap("resource", "/select", + checkRules(Map.of("resource", "/select", "httpMethod", "GET", "handler", new SearchHandler(), "collectionRequests", singletonList(new CollectionRequest("mycoll")), @@ -210,94 +208,89 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { setUserRole("cio", "su"); addPermission("all", "su"); - checkRules(makeMap("resource", ReplicationHandler.PATH, + checkRules(Map.of("resource", ReplicationHandler.PATH, "httpMethod", "POST", "userPrincipal", "tim", "handler", new ReplicationHandler(), "collectionRequests", singletonList(new CollectionRequest("mycoll")) ) , FORBIDDEN); - checkRules(makeMap("resource", ReplicationHandler.PATH, + checkRules(Map.of("resource", ReplicationHandler.PATH, "httpMethod", "POST", "userPrincipal", "cio", "handler", new ReplicationHandler(), "collectionRequests", singletonList(new CollectionRequest("mycoll")) ) , STATUS_OK); - checkRules(makeMap("resource", "/admin/collections", + checkRules(Map.of("resource", "/admin/collections", "userPrincipal", "tim", "requestType", AuthorizationContext.RequestType.ADMIN, - "collectionRequests", null, "handler", new CollectionsHandler(), "params", new MapSolrParams(singletonMap("action", "CREATE"))) , STATUS_OK); + } - resetPermissionsAndRoles(); + @Test + public void testCoreAdminPermissions() { addPermission("core-admin-edit", "su"); addPermission("core-admin-read", "user"); setUserRole("cio", "su"); addPermission("all", "su"); - checkRules(makeMap("resource", "/admin/cores", - "userPrincipal", null, + checkRules(Map.of("resource", "/admin/cores", "requestType", RequestType.ADMIN, - "collectionRequests", null, "handler", new CoreAdminHandler(null), "params", new MapSolrParams(singletonMap("action", "CREATE"))) , PROMPT_FOR_CREDENTIALS); - checkRules(makeMap("resource", "/admin/cores", + checkRules(Map.of("resource", "/admin/cores", "userPrincipal", "joe", "requestType", RequestType.ADMIN, - "collectionRequests", null, "handler", new CoreAdminHandler(null), "params", new MapSolrParams(singletonMap("action", "CREATE"))) , FORBIDDEN); - checkRules(makeMap("resource", "/admin/cores", + checkRules(Map.of("resource", "/admin/cores", "userPrincipal", "joe", "requestType", RequestType.ADMIN, - "collectionRequests", null, "handler", new CoreAdminHandler(null), "params", new MapSolrParams(singletonMap("action", "STATUS"))) , STATUS_OK); - checkRules(makeMap("resource", "/admin/cores", + checkRules(Map.of("resource", "/admin/cores", "userPrincipal", "cio", "requestType", RequestType.ADMIN, - "collectionRequests", null, "handler", new CoreAdminHandler(null), "params", new MapSolrParams(singletonMap("action", "CREATE"))) ,STATUS_OK); + } - resetPermissionsAndRoles(); - addPermission("test-params", "admin", "/x", makeMap("key", Arrays.asList("REGEX:(?i)val1", "VAL2"))); + @Test + public void testParamsPermissions() { + addPermission("test-params", "admin", "/x", Map.of("key", Arrays.asList("REGEX:(?i)val1", "VAL2"))); - checkRules(makeMap("resource", "/x", - "userPrincipal", null, + checkRules(Map.of("resource", "/x", "requestType", RequestType.UNKNOWN, "collectionRequests", "go", "handler", new DumpRequestHandler(), "params", new MapSolrParams(singletonMap("key", "VAL1"))) , PROMPT_FOR_CREDENTIALS); - checkRules(makeMap("resource", "/x", - "userPrincipal", null, + checkRules(Map.of("resource", "/x", "requestType", RequestType.UNKNOWN, "collectionRequests", "go", "handler", new DumpRequestHandler(), "params", new MapSolrParams(singletonMap("key", "Val1"))) , PROMPT_FOR_CREDENTIALS); - checkRules(makeMap("resource", "/x", - "userPrincipal", null, + checkRules(Map.of("resource", "/x", "requestType", RequestType.UNKNOWN, "collectionRequests", "go", "handler", new DumpRequestHandler(), "params", new MapSolrParams(singletonMap("key", "Val1"))) , PROMPT_FOR_CREDENTIALS); - checkRules(makeMap("resource", "/x", + checkRules(Map.of("resource", "/x", "userPrincipal", "joe", "requestType", RequestType.UNKNOWN, "collectionRequests", "go", @@ -305,7 +298,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { "params", new MapSolrParams(singletonMap("key", "Val1"))) , FORBIDDEN); - checkRules(makeMap("resource", "/x", + checkRules(Map.of("resource", "/x", "userPrincipal", "joe", "requestType", RequestType.UNKNOWN, "collectionRequests", "go", @@ -313,26 +306,24 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { "params", new MapSolrParams(singletonMap("key", "Val2"))) , STATUS_OK); - checkRules(makeMap("resource", "/x", + checkRules(Map.of("resource", "/x", "userPrincipal", "joe", "requestType", RequestType.UNKNOWN, "collectionRequests", "go", "handler", new DumpRequestHandler(), "params", new MapSolrParams(singletonMap("key", "VAL2"))) , FORBIDDEN); + } + @Test + public void testCustomRules() { Map customRules = (Map) Utils.fromJSONString( "{permissions:[" + " {name:update, role:[admin_role,update_role]}," + " {name:read, role:[admin_role,update_role,read_role]}" + "]}"); - clearUserRoles(); - setUserRole("admin", "admin_role"); - setUserRole("update", "update_role"); - setUserRole("solr", "read_role"); - - checkRules(makeMap("resource", "/update", + checkRules(Map.of("resource", "/update", "userPrincipal", "solr", "requestType", RequestType.UNKNOWN, "collectionRequests", "go", @@ -353,7 +344,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { setUserRole("dev", "dev"); setUserRole("admin", "admin"); addPermission("all", "dev", "admin"); - checkRules(makeMap("resource", "/update", + checkRules(Map.of("resource", "/update", "userPrincipal", "dev", "requestType", RequestType.UNKNOWN, "collectionRequests", "go", @@ -363,7 +354,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { handler = new PropertiesRequestHandler(); assertThat(handler, new IsNot<>(new IsInstanceOf(PermissionNameProvider.class))); - checkRules(makeMap("resource", "/admin/info/properties", + checkRules(Map.of("resource", "/admin/info/properties", "userPrincipal", "dev", "requestType", RequestType.UNKNOWN, "collectionRequests", "go", @@ -385,7 +376,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { setUserRole("dev", "dev"); setUserRole("admin", "admin"); addPermission("all", "*"); - checkRules(makeMap("resource", "/update", + checkRules(Map.of("resource", "/update", "userPrincipal", "dev", "requestType", RequestType.UNKNOWN, "collectionRequests", "go", @@ -395,7 +386,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { handler = new PropertiesRequestHandler(); assertThat(handler, new IsNot<>(new IsInstanceOf(PermissionNameProvider.class))); - checkRules(makeMap("resource", "/admin/info/properties", + checkRules(Map.of("resource", "/admin/info/properties", "userPrincipal", "dev", "requestType", RequestType.UNKNOWN, "collectionRequests", "go", @@ -416,7 +407,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { setUserRole("dev", "dev"); setUserRole("admin", "admin"); addPermission("all", "admin"); - checkRules(makeMap("resource", "/update", + checkRules(Map.of("resource", "/update", "userPrincipal", "dev", "requestType", RequestType.UNKNOWN, "collectionRequests", "go", @@ -426,7 +417,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { handler = new PropertiesRequestHandler(); assertThat(handler, new IsNot<>(new IsInstanceOf(PermissionNameProvider.class))); - checkRules(makeMap("resource", "/admin/info/properties", + checkRules(Map.of("resource", "/admin/info/properties", "userPrincipal", "dev", "requestType", RequestType.UNKNOWN, "collectionRequests", "go", @@ -435,22 +426,36 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { , FORBIDDEN); } - void addPermission(String permissionName, String role, String path, Map params) { - ((List)rules.get("permissions")).add( makeMap("name", permissionName, "role", role, "path", path, "params", params)); + @Test + public void testShortNameResolvesPermissions() { + assumeThat("ExternalRBAPlugin doesn't use short name", + createPlugin(), is(instanceOf(RuleBasedAuthorizationPlugin.class))); + + setUserRole("admin", "admin"); + addPermission("all", "admin"); + + Map values = Map.of( + "userPrincipal", "admin@EXAMPLE", + "userName", "admin", + "resource", "/admin/info/properties", + "requestType", RequestType.ADMIN, + "handler", new PropertiesRequestHandler()); + + // Short names disabled, admin should fail, admin@EXAMPLE should succeed + rules.put("useShortName", "false"); + checkRules(values, FORBIDDEN); + + // Short names enabled, admin should succeed, admin@EXAMPLE should fail + rules.put("useShortName", "true"); + checkRules(values, STATUS_OK); } - void removePermission(String name) { - List> oldPerm = ((List) rules.get("permissions")); - List> newPerm = oldPerm.stream().filter(p -> !p.get("name").equals(name)).collect(Collectors.toList()); - rules.put("permissions", newPerm); + void addPermission(String permissionName, String role, String path, Map params) { + ((List)rules.get("permissions")).add(Map.of("name", permissionName, "role", role, "path", path, "params", params)); } protected void addPermission(String permissionName, String... roles) { - ((List)rules.get("permissions")).add( makeMap("name", permissionName, "role", Arrays.asList(roles))); - } - - void clearUserRoles() { - rules.put("user-role", new HashMap()); + ((List)rules.get("permissions")).add(Map.of("name", permissionName, "role", Arrays.asList(roles))); } protected void setUserRole(String user, String role) { @@ -465,8 +470,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { assertEquals("admin", perms.getVal("permissions[0]/role")); perms.runCmd("{set-permission : {name: config-edit, role: [admin, dev], index:2 } }", false); perms.runCmd("{set-permission : {name: config-edit, role: [admin, dev], index:1}}", true); - @SuppressWarnings({"rawtypes"}) - Collection roles = (Collection) perms.getVal("permissions[0]/role"); + Collection roles = (Collection) perms.getVal("permissions[0]/role"); assertEquals(2, roles.size()); assertTrue(roles.contains("admin")); assertTrue(roles.contains("dev")); @@ -485,16 +489,13 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { assertEquals("y", perms.getVal("permissions[1]/collection")); } - static class Perms { - @SuppressWarnings({"rawtypes"}) - Map conf = new HashMap<>(); + static class Perms { + Map conf = new HashMap<>(); ConfigEditablePlugin plugin = new RuleBasedAuthorizationPlugin(); List parsedCommands; public void runCmd(String cmds, boolean failOnError) throws IOException { parsedCommands = CommandOperation.parse(new StringReader(cmds)); - @SuppressWarnings({"rawtypes"}) - LinkedList ll = new LinkedList(); Map edited = plugin.edit(conf, parsedCommands); if(edited!= null) conf = edited; @SuppressWarnings({"rawtypes"}) @@ -536,10 +537,15 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { Object userPrincipal = values.get("userPrincipal"); return userPrincipal == null ? null : new BasicUserPrincipal(String.valueOf(userPrincipal)); } + + @Override + public String getUserName() { + return String.valueOf(values.get("userName")); + } }; } - protected abstract class MockAuthorizationContext extends AuthorizationContext { + protected abstract static class MockAuthorizationContext extends AuthorizationContext { private final Map values; public MockAuthorizationContext(Map values) { @@ -552,14 +558,19 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 { return params == null ? new MapSolrParams(new HashMap<>()) : params; } + @Override + public String getUserName() { + Principal userPrincipal = getUserPrincipal(); + return userPrincipal == null ? null : userPrincipal.getName(); + } + @Override public String getHttpHeader(String header) { return null; } @Override - @SuppressWarnings({"rawtypes"}) - public Enumeration getHeaderNames() { + public Enumeration getHeaderNames() { return null; } diff --git a/solr/core/src/test/org/apache/solr/security/hadoop/KerberosUtils.java b/solr/core/src/test/org/apache/solr/security/hadoop/KerberosUtils.java new file mode 100644 index 00000000000..4d1b4b4853c --- /dev/null +++ b/solr/core/src/test/org/apache/solr/security/hadoop/KerberosUtils.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.security.hadoop; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.apache.solr.cloud.KerberosTestServices; + +/** + * A utility class which provides common functionality required to test kerberos integration. + */ +public class KerberosUtils { + /** + * This method sets up Hadoop mini-kdc along with relevant Kerberos configuration files + * (e.g. jaas.conf) as well as system properties. + * + * @param baseDir The directory path which should be used by the Hadoop mini-kdc + * @return An instance of {@link KerberosTestServices} + * @throws Exception in case of errors. + */ + static KerberosTestServices setupMiniKdc(Path baseDir) throws Exception { + System.setProperty("solr.jaas.debug", "true"); + Path kdcDir = baseDir.resolve("minikdc"); + String solrClientPrincipal = "solr"; + File keytabFile = kdcDir.resolve("keytabs").toFile(); + KerberosTestServices tmp = KerberosTestServices.builder() + .withKdc(kdcDir.toFile()) + .withJaasConfiguration(solrClientPrincipal, keytabFile, "SolrClient") + .build(); + String solrServerPrincipal = "HTTP/127.0.0.1"; + tmp.start(); + tmp.getKdc().createPrincipal(keytabFile, solrServerPrincipal, solrClientPrincipal); + + String appName = "SolrClient"; + String jaas = appName + " {\n" + + " com.sun.security.auth.module.Krb5LoginModule required\n" + + " useKeyTab=true\n" + + " keyTab=\"" + keytabFile.getAbsolutePath() + "\"\n" + + " storeKey=true\n" + + " useTicketCache=false\n" + + " doNotPrompt=true\n" + + " debug=true\n" + + " principal=\"" + solrClientPrincipal + "\";\n" + + "};"; + + Path jaasFile = kdcDir.resolve("jaas-client.conf"); + Files.writeString(jaasFile, jaas); + System.setProperty("java.security.auth.login.config", jaasFile.toString()); + System.setProperty("solr.kerberos.jaas.appname", appName); + + System.setProperty("solr.kerberos.principal", solrServerPrincipal); + System.setProperty("solr.kerberos.keytab", keytabFile.getAbsolutePath()); + // Extracts 127.0.0.1 from HTTP/127.0.0.1@EXAMPLE.COM + System.setProperty("solr.kerberos.name.rules", "RULE:[1:$1@$0](.*EXAMPLE.COM)s/@.*//" + + "\nRULE:[2:$2@$0](.*EXAMPLE.COM)s/@.*//" + + "\nDEFAULT" + ); + + return tmp; + } + + /** + * This method stops the Hadoop mini-kdc instance as well as cleanup relevant Java system properties. + * + * @param kerberosTestServices An instance of Hadoop mini-kdc + */ + public static void cleanupMiniKdc(KerberosTestServices kerberosTestServices) { + System.clearProperty("java.security.auth.login.config"); + System.clearProperty("solr.kerberos.principal"); + System.clearProperty("solr.kerberos.keytab"); + System.clearProperty("solr.kerberos.name.rules"); + System.clearProperty("solr.jaas.debug"); + if (kerberosTestServices != null) { + kerberosTestServices.stop(); + } + } +} diff --git a/solr/core/src/test/org/apache/solr/security/hadoop/TestRuleBasedAuthorizationWithKerberos.java b/solr/core/src/test/org/apache/solr/security/hadoop/TestRuleBasedAuthorizationWithKerberos.java new file mode 100644 index 00000000000..0020e54940e --- /dev/null +++ b/solr/core/src/test/org/apache/solr/security/hadoop/TestRuleBasedAuthorizationWithKerberos.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.security.hadoop; + +import org.apache.lucene.util.Constants; +import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.impl.CloudSolrClient; +import org.apache.solr.client.solrj.request.CollectionAdminRequest; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.cloud.AbstractDistribZkTestBase; +import org.apache.solr.cloud.KerberosTestServices; +import org.apache.solr.cloud.SolrCloudTestCase; +import org.apache.solr.common.SolrInputDocument; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestRuleBasedAuthorizationWithKerberos extends SolrCloudTestCase { + protected static final int NUM_SERVERS = 1; + protected static final int NUM_SHARDS = 1; + protected static final int REPLICATION_FACTOR = 1; + private static KerberosTestServices kerberosTestServices; + + @BeforeClass + public static void setupClass() throws Exception { + assumeFalse("Hadoop does not work on Windows", Constants.WINDOWS); + + kerberosTestServices = KerberosUtils.setupMiniKdc(createTempDir()); + + configureCluster(NUM_SERVERS)// nodes + .withSecurityJson(TEST_PATH().resolve("security").resolve("hadoop_kerberos_authz_config.json")) + .addConfig("conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf")) + .configure(); + } + + @AfterClass + public static void tearDownClass() { + KerberosUtils.cleanupMiniKdc(kerberosTestServices); + kerberosTestServices = null; + } + + @Test + public void testCollectionCreateSearchDelete() throws Exception { + CloudSolrClient solrClient = cluster.getSolrClient(); + String collectionName = "testkerberoscollection_authz"; + + // create collection + CollectionAdminRequest.Create create = CollectionAdminRequest.createCollection(collectionName, "conf1", + NUM_SHARDS, REPLICATION_FACTOR); + create.process(solrClient); + + SolrInputDocument doc = new SolrInputDocument(); + doc.setField("id", "1"); + solrClient.add(collectionName, doc); + solrClient.commit(collectionName); + + SolrQuery query = new SolrQuery(); + query.setQuery("*:*"); + QueryResponse rsp = solrClient.query(collectionName, query); + assertEquals(1, rsp.getResults().getNumFound()); + + CollectionAdminRequest.Delete deleteReq = CollectionAdminRequest.deleteCollection(collectionName); + deleteReq.process(solrClient); + AbstractDistribZkTestBase.waitForCollectionToDisappear(collectionName, + solrClient.getZkStateReader(), true, 330); + } +} \ No newline at end of file diff --git a/solr/core/src/test/org/apache/solr/security/hadoop/TestSolrCloudWithHadoopAuthPlugin.java b/solr/core/src/test/org/apache/solr/security/hadoop/TestSolrCloudWithHadoopAuthPlugin.java index d75497ed992..27f869e6c39 100644 --- a/solr/core/src/test/org/apache/solr/security/hadoop/TestSolrCloudWithHadoopAuthPlugin.java +++ b/solr/core/src/test/org/apache/solr/security/hadoop/TestSolrCloudWithHadoopAuthPlugin.java @@ -16,10 +16,6 @@ */ package org.apache.solr.security.hadoop; -import java.io.File; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.io.FileUtils; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.client.solrj.request.CollectionAdminRequest; @@ -43,7 +39,7 @@ public class TestSolrCloudWithHadoopAuthPlugin extends SolrCloudAuthTestCase { public static void setupClass() throws Exception { HdfsTestUtil.checkAssumptions(); - setupMiniKdc(); + kerberosTestServices = KerberosUtils.setupMiniKdc(createTempDir()); configureCluster(NUM_SERVERS)// nodes .withSecurityJson(TEST_PATH().resolve("security").resolve("hadoop_kerberos_config.json")) @@ -54,55 +50,10 @@ public class TestSolrCloudWithHadoopAuthPlugin extends SolrCloudAuthTestCase { @AfterClass public static void tearDownClass() throws Exception { - System.clearProperty("java.security.auth.login.config"); - System.clearProperty("solr.kerberos.principal"); - System.clearProperty("solr.kerberos.keytab"); - System.clearProperty("solr.kerberos.name.rules"); - System.clearProperty("solr.jaas.debug"); - if (kerberosTestServices != null) { - kerberosTestServices.stop(); - } + KerberosUtils.cleanupMiniKdc(kerberosTestServices); kerberosTestServices = null; } - private static void setupMiniKdc() throws Exception { - System.setProperty("solr.jaas.debug", "true"); - String kdcDir = createTempDir()+File.separator+"minikdc"; - String solrClientPrincipal = "solr"; - File keytabFile = new File(kdcDir, "keytabs"); - kerberosTestServices = KerberosTestServices.builder() - .withKdc(new File(kdcDir)) - .withJaasConfiguration(solrClientPrincipal, keytabFile, "SolrClient") - .build(); - String solrServerPrincipal = "HTTP/127.0.0.1"; - kerberosTestServices.start(); - kerberosTestServices.getKdc().createPrincipal(keytabFile, solrServerPrincipal, solrClientPrincipal); - - String jaas = "SolrClient {\n" - + " com.sun.security.auth.module.Krb5LoginModule required\n" - + " useKeyTab=true\n" - + " keyTab=\"" + keytabFile.getAbsolutePath() + "\"\n" - + " storeKey=true\n" - + " useTicketCache=false\n" - + " doNotPrompt=true\n" - + " debug=true\n" - + " principal=\"" + solrClientPrincipal + "\";\n" - + "};"; - - String jaasFilePath = kdcDir+File.separator+"jaas-client.conf"; - FileUtils.write(new File(jaasFilePath), jaas, StandardCharsets.UTF_8); - System.setProperty("java.security.auth.login.config", jaasFilePath); - System.setProperty("solr.kerberos.jaas.appname", "SolrClient"); // Get this app name from the jaas file - - System.setProperty("solr.kerberos.principal", solrServerPrincipal); - System.setProperty("solr.kerberos.keytab", keytabFile.getAbsolutePath()); - // Extracts 127.0.0.1 from HTTP/127.0.0.1@EXAMPLE.COM - System.setProperty("solr.kerberos.name.rules", "RULE:[1:$1@$0](.*EXAMPLE.COM)s/@.*//" - + "\nRULE:[2:$2@$0](.*EXAMPLE.COM)s/@.*//" - + "\nDEFAULT" - ); - } - @Test public void testBasics() throws Exception { testCollectionCreateSearchDelete(); diff --git a/solr/solr-ref-guide/src/rule-based-authorization-plugin.adoc b/solr/solr-ref-guide/src/rule-based-authorization-plugin.adoc index 935412bb2a5..f75336aeb7b 100644 --- a/solr/solr-ref-guide/src/rule-based-authorization-plugin.adoc +++ b/solr/solr-ref-guide/src/rule-based-authorization-plugin.adoc @@ -74,7 +74,6 @@ The syntax for individual permissions is more involved and is treated in greater User's roles may either come from the request itself, then you will use the `ExternalRoleRuleBasedAuthorizationPlugin` variant of RBAC. If you need to hardcode user-role mappings, then you need to use the `RuleBasedAuthorizationPlugin` and define the user-role mappings in `security.json` like this: user-role:: A mapping of individual users to the roles they belong to. The value of this property is a JSON map, where each property name is a user, and each property value is either the name of a single role or a JSON array of multiple roles that the specified user belongs to. For example: -+ [source,json] ---- "user-role": { @@ -82,6 +81,11 @@ user-role:: A mapping of individual users to the roles they belong to. The valu "user2": ["role1", "role2"] } ---- +useShortName:: Determines if user-role mappings will resolve using the full principal or a shortened name provided by the authentication plugin. For example, the `KerberosAuthPlugin` may provide a full principal as `user@EXAMPLE.COM`, while the corresponding short name would be `user`. ++ +For some plugins the principal name and short name may be the same. ++ +This setting is optional and defaults to `false` if unset. === Example for RuleBasedAuthorizationPlugin and BasicAuth