mirror of https://github.com/apache/lucene.git
SOLR-10814 Add short-name feature to RuleBasedAuthz plugin
Additional-Author: Hrishikesh Gadre <hgadre@cloudera.com>
This commit is contained in:
parent
06b1f3e866
commit
d3f4b21deb
|
@ -96,11 +96,20 @@ public abstract class AuthenticationPlugin implements SolrInfoBean {
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpServletRequest wrapWithPrincipal(HttpServletRequest request, Principal principal) {
|
HttpServletRequest wrapWithPrincipal(HttpServletRequest request, Principal principal) {
|
||||||
|
return wrapWithPrincipal(request, principal, principal.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpServletRequest wrapWithPrincipal(HttpServletRequest request, Principal principal, String username) {
|
||||||
return new HttpServletRequestWrapper(request) {
|
return new HttpServletRequestWrapper(request) {
|
||||||
@Override
|
@Override
|
||||||
public Principal getUserPrincipal() {
|
public Principal getUserPrincipal() {
|
||||||
return principal;
|
return principal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRemoteUser() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,13 +41,35 @@ public abstract class AuthorizationContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract SolrParams getParams() ;
|
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() ;
|
public abstract Principal getUserPrincipal() ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the name of the authenticated user for the current request.
|
||||||
|
* The return value of this method is agnostic of the underlying authentication
|
||||||
|
* mechanism used.
|
||||||
|
*
|
||||||
|
* @return user name in case of an authenticated user
|
||||||
|
* null in case of unauthenticated request
|
||||||
|
*/
|
||||||
|
public abstract String getUserName();
|
||||||
|
|
||||||
public abstract String getHttpHeader(String header);
|
public abstract String getHttpHeader(String header);
|
||||||
|
|
||||||
@SuppressWarnings({"rawtypes"})
|
public abstract Enumeration<String> getHeaderNames();
|
||||||
public abstract Enumeration getHeaderNames();
|
|
||||||
|
|
||||||
public abstract String getRemoteAddr();
|
public abstract String getRemoteAddr();
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ public class BasicAuthPlugin extends AuthenticationPlugin implements ConfigEdita
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
Principal principal = new BasicAuthUserPrincipal(username, pwd);
|
Principal principal = new BasicAuthUserPrincipal(username, pwd);
|
||||||
request = wrapWithPrincipal(request, principal);
|
request = wrapWithPrincipal(request, principal, username);
|
||||||
numAuthenticated.inc();
|
numAuthenticated.inc();
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package org.apache.solr.security;
|
package org.apache.solr.security;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -49,8 +48,6 @@ import org.apache.solr.common.cloud.ZkCredentialsProvider;
|
||||||
import org.apache.zookeeper.CreateMode;
|
import org.apache.zookeeper.CreateMode;
|
||||||
import org.apache.zookeeper.KeeperException;
|
import org.apache.zookeeper.KeeperException;
|
||||||
import org.apache.zookeeper.data.ACL;
|
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}.
|
* 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.
|
* application to reuse the authentication of an end-user or another application.
|
||||||
*/
|
*/
|
||||||
public class DelegationTokenKerberosFilter extends DelegationTokenAuthenticationFilter {
|
public class DelegationTokenKerberosFilter extends DelegationTokenAuthenticationFilter {
|
||||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
|
||||||
|
|
||||||
private CuratorFramework curatorFramework;
|
private CuratorFramework curatorFramework;
|
||||||
private final Locale defaultLocale = Locale.getDefault();
|
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.
|
* "solr.impersonator.user.name" will be added to the configuration.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Configuration getProxyuserConfiguration(FilterConfig filterConf)
|
protected Configuration getProxyuserConfiguration(FilterConfig filterConf) {
|
||||||
throws ServletException {
|
|
||||||
Configuration conf = new Configuration(false);
|
Configuration conf = new Configuration(false);
|
||||||
|
|
||||||
Enumeration<?> names = filterConf.getInitParameterNames();
|
Enumeration<String> names = filterConf.getInitParameterNames();
|
||||||
while (names.hasMoreElements()) {
|
while (names.hasMoreElements()) {
|
||||||
String name = (String) names.nextElement();
|
String name = names.nextElement();
|
||||||
if (name.startsWith(KerberosPlugin.IMPERSONATOR_PREFIX)) {
|
if (name.startsWith(KerberosPlugin.IMPERSONATOR_PREFIX)) {
|
||||||
String value = filterConf.getInitParameter(name);
|
String value = filterConf.getInitParameter(name);
|
||||||
conf.set(PROXYUSER_PREFIX + "." + name.substring(KerberosPlugin.IMPERSONATOR_PREFIX.length()), value);
|
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
|
// without the appropriate ACL configuration. This issue is possibly related to HADOOP-11973
|
||||||
try {
|
try {
|
||||||
zkClient.makePath(SecurityAwareZkACLProvider.SECURITY_ZNODE_PATH, CreateMode.PERSISTENT, true);
|
zkClient.makePath(SecurityAwareZkACLProvider.SECURITY_ZNODE_PATH, CreateMode.PERSISTENT, true);
|
||||||
|
} catch (KeeperException.NodeExistsException ex) {
|
||||||
} catch (KeeperException ex) {
|
// ignore?
|
||||||
if (ex.code() != KeeperException.Code.NODEEXISTS) {
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
curatorFramework = CuratorFrameworkFactory.builder()
|
curatorFramework = CuratorFrameworkFactory.builder()
|
||||||
|
|
|
@ -16,24 +16,19 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.solr.security;
|
package org.apache.solr.security;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.solr.common.SolrException;
|
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
|
* Rule Based Authz plugin implementation which reads user roles from the request. This requires
|
||||||
* a Principal implementing VerifiedUserRoles interface, e.g. JWTAuthenticationPlugin
|
* a Principal implementing VerifiedUserRoles interface, e.g. JWTAuthenticationPlugin
|
||||||
*/
|
*/
|
||||||
public class ExternalRoleRuleBasedAuthorizationPlugin extends RuleBasedAuthorizationPluginBase {
|
public class ExternalRoleRuleBasedAuthorizationPlugin extends RuleBasedAuthorizationPluginBase {
|
||||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
@Override
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(Map<String, Object> initInfo) {
|
public void init(Map<String, Object> initInfo) {
|
||||||
super.init(initInfo);
|
super.init(initInfo);
|
||||||
if (initInfo.containsKey("user-role")) {
|
if (initInfo.containsKey("user-role")) {
|
||||||
|
|
|
@ -114,9 +114,7 @@ public class KerberosPlugin extends AuthenticationPlugin implements HttpClientBu
|
||||||
putParamOptional(params, "kerberos.keytab", KEYTAB_PARAM);
|
putParamOptional(params, "kerberos.keytab", KEYTAB_PARAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
String delegationTokenStr = System.getProperty(DELEGATION_TOKEN_ENABLED, null);
|
boolean delegationTokenEnabled = Boolean.getBoolean(DELEGATION_TOKEN_ENABLED);
|
||||||
boolean delegationTokenEnabled =
|
|
||||||
(delegationTokenStr == null) ? false : Boolean.parseBoolean(delegationTokenStr);
|
|
||||||
ZkController controller = coreContainer.getZkController();
|
ZkController controller = coreContainer.getZkController();
|
||||||
|
|
||||||
if (delegationTokenEnabled) {
|
if (delegationTokenEnabled) {
|
||||||
|
@ -162,7 +160,7 @@ public class KerberosPlugin extends AuthenticationPlugin implements HttpClientBu
|
||||||
}
|
}
|
||||||
|
|
||||||
// check impersonator config
|
// 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();
|
String key = e.nextElement().toString();
|
||||||
if (key.startsWith(IMPERSONATOR_PREFIX)) {
|
if (key.startsWith(IMPERSONATOR_PREFIX)) {
|
||||||
if (!delegationTokenEnabled) {
|
if (!delegationTokenEnabled) {
|
||||||
|
|
|
@ -16,15 +16,11 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.solr.security;
|
package org.apache.solr.security;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import static org.apache.solr.handler.admin.SecurityConfHandler.getMapValue;
|
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
|
* mapping in the security.json configuration
|
||||||
*/
|
*/
|
||||||
public class RuleBasedAuthorizationPlugin extends RuleBasedAuthorizationPluginBase {
|
public class RuleBasedAuthorizationPlugin extends RuleBasedAuthorizationPluginBase {
|
||||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
|
||||||
|
|
||||||
private final Map<String, Set<String>> usersVsRoles = new HashMap<>();
|
private final Map<String, Set<String>> usersVsRoles = new HashMap<>();
|
||||||
|
private boolean useShortName;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Map<String, Object> initInfo) {
|
public void init(Map<String, Object> initInfo) {
|
||||||
super.init(initInfo);
|
super.init(initInfo);
|
||||||
Map<String, Object> map = getMapValue(initInfo, "user-role");
|
Map<String, Object> map = getMapValue(initInfo, "user-role");
|
||||||
for (Object o : map.entrySet()) {
|
for (Object o : map.entrySet()) {
|
||||||
@SuppressWarnings({"rawtypes"})
|
@SuppressWarnings("unchecked")
|
||||||
Map.Entry e = (Map.Entry) o;
|
Map.Entry<String, ?> e = (Map.Entry<String, ?>) o;
|
||||||
String roleName = (String) e.getKey();
|
String roleName = e.getKey();
|
||||||
usersVsRoles.put(roleName, Permission.readValueAsSet(map, roleName));
|
usersVsRoles.put(roleName, Permission.readValueAsSet(map, roleName));
|
||||||
}
|
}
|
||||||
|
useShortName = Boolean.parseBoolean(initInfo.getOrDefault("useShortName", Boolean.FALSE).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> 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
|
* @param principal the user Principal from the request
|
||||||
* @return set of roles as strings
|
* @return set of roles as strings
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.io.IOException;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -34,8 +35,6 @@ import org.apache.solr.common.util.CommandOperation;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.function.Function.identity;
|
||||||
import static java.util.stream.Collectors.toMap;
|
import static java.util.stream.Collectors.toMap;
|
||||||
import static org.apache.solr.handler.admin.SecurityConfHandler.getListValue;
|
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 static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||||
|
|
||||||
private final Map<String, WildCardSupportMap> mapping = new HashMap<>();
|
private final Map<String, WildCardSupportMap> mapping = new HashMap<>();
|
||||||
private final List<Permission> permissions = new ArrayList<>();
|
|
||||||
|
|
||||||
|
// Doesn't implement Map because we violate the contracts of put() and get()
|
||||||
private static class WildCardSupportMap extends HashMap<String, List<Permission>> {
|
private static class WildCardSupportMap {
|
||||||
final Set<String> wildcardPrefixes = new HashSet<>();
|
final Set<String> wildcardPrefixes = new HashSet<>();
|
||||||
|
final Map<String, List<Permission>> delegate = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Permission> put(String key, List<Permission> value) {
|
public List<Permission> put(String key, List<Permission> value) {
|
||||||
if (key != null && key.endsWith("/*")) {
|
if (key != null && key.endsWith("/*")) {
|
||||||
key = key.substring(0, key.length() - 2);
|
key = key.substring(0, key.length() - 2);
|
||||||
wildcardPrefixes.add(key);
|
wildcardPrefixes.add(key);
|
||||||
}
|
}
|
||||||
return super.put(key, value);
|
return delegate.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public List<Permission> get(String key) {
|
||||||
public List<Permission> get(Object key) {
|
List<Permission> result = delegate.get(key);
|
||||||
List<Permission> result = super.get(key);
|
|
||||||
if (key == null || result != null) return result;
|
if (key == null || result != null) return result;
|
||||||
if (!wildcardPrefixes.isEmpty()) {
|
|
||||||
for (String s : wildcardPrefixes) {
|
for (String s : wildcardPrefixes) {
|
||||||
if (key.toString().startsWith(s)) {
|
if (key.startsWith(s)) {
|
||||||
List<Permission> l = super.get(s);
|
List<Permission> wildcardPermissions = delegate.get(s);
|
||||||
if (l != null) {
|
if (wildcardPermissions != null) {
|
||||||
result = result == null ? new ArrayList<>() : new ArrayList<>(result);
|
if (result == null) result = new ArrayList<>();
|
||||||
result.addAll(l);
|
result.addAll(wildcardPermissions);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<String> keySet() {
|
||||||
|
return delegate.keySet();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -109,8 +110,7 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP
|
||||||
return flag.rsp;
|
return flag.rsp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MatchStatus checkCollPerm(Map<String, List<Permission>> pathVsPerms,
|
private MatchStatus checkCollPerm(WildCardSupportMap pathVsPerms, AuthorizationContext context) {
|
||||||
AuthorizationContext context) {
|
|
||||||
if (pathVsPerms == null) return MatchStatus.NO_PERMISSIONS_FOUND;
|
if (pathVsPerms == null) return MatchStatus.NO_PERMISSIONS_FOUND;
|
||||||
|
|
||||||
if (log.isTraceEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
|
@ -131,7 +131,6 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP
|
||||||
if (permissions == null || permissions.isEmpty()) {
|
if (permissions == null || permissions.isEmpty()) {
|
||||||
return MatchStatus.NO_PERMISSIONS_FOUND;
|
return MatchStatus.NO_PERMISSIONS_FOUND;
|
||||||
}
|
}
|
||||||
Principal principal = context.getUserPrincipal();
|
|
||||||
|
|
||||||
log.trace("Following perms are associated with this collection and path: [{}]", permissions);
|
log.trace("Following perms are associated with this collection and path: [{}]", permissions);
|
||||||
final Permission governingPermission = findFirstGoverningPermission(permissions, context);
|
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());
|
log.debug("Found perm [{}] to govern resource [{}]", governingPermission, context.getResource());
|
||||||
}
|
}
|
||||||
|
|
||||||
return determineIfPermissionPermitsPrincipal(principal, governingPermission);
|
return determineIfPermissionPermitsPrincipal(context, governingPermission);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Permission findFirstGoverningPermission(List<Permission> permissions, AuthorizationContext context) {
|
private Permission findFirstGoverningPermission(List<Permission> permissions, AuthorizationContext context) {
|
||||||
|
@ -216,11 +215,12 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MatchStatus determineIfPermissionPermitsPrincipal(Principal principal, Permission governingPermission) {
|
private MatchStatus determineIfPermissionPermitsPrincipal(AuthorizationContext context, Permission governingPermission) {
|
||||||
if (governingPermission.role == null) {
|
if (governingPermission.role == null) {
|
||||||
log.debug("Governing permission [{}] has no role; permitting access", governingPermission);
|
log.debug("Governing permission [{}] has no role; permitting access", governingPermission);
|
||||||
return MatchStatus.PERMITTED;
|
return MatchStatus.PERMITTED;
|
||||||
}
|
}
|
||||||
|
Principal principal = context.getUserPrincipal();
|
||||||
if (principal == null) {
|
if (principal == null) {
|
||||||
log.debug("Governing permission [{}] has role, but request principal cannot be identified; forbidding access", governingPermission);
|
log.debug("Governing permission [{}] has role, but request principal cannot be identified; forbidding access", governingPermission);
|
||||||
return MatchStatus.USER_REQUIRED;
|
return MatchStatus.USER_REQUIRED;
|
||||||
|
@ -229,7 +229,7 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP
|
||||||
return MatchStatus.PERMITTED;
|
return MatchStatus.PERMITTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> userRoles = getUserRoles(principal);
|
Set<String> userRoles = getUserRoles(context);
|
||||||
for (String role : governingPermission.role) {
|
for (String role : governingPermission.role) {
|
||||||
if (userRoles != null && userRoles.contains(role)) {
|
if (userRoles != null && userRoles.contains(role)) {
|
||||||
log.debug("Governing permission [{}] allows access to role [{}]; permitting access", governingPermission, role);
|
log.debug("Governing permission [{}] allows access to role [{}]; permitting access", governingPermission, role);
|
||||||
|
@ -260,7 +260,7 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings({"unchecked"})
|
@SuppressWarnings({"unchecked"})
|
||||||
public void init(@SuppressWarnings({"rawtypes"})Map<String, Object> initInfo) {
|
public void init(Map<String, Object> initInfo) {
|
||||||
mapping.put(null, new WildCardSupportMap());
|
mapping.put(null, new WildCardSupportMap());
|
||||||
@SuppressWarnings({"rawtypes"})
|
@SuppressWarnings({"rawtypes"})
|
||||||
List<Map> perms = getListValue(initInfo, "permissions");
|
List<Map> perms = getListValue(initInfo, "permissions");
|
||||||
|
@ -272,7 +272,6 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP
|
||||||
log.error("Invalid permission ", exp);
|
log.error("Invalid permission ", exp);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
permissions.add(p);
|
|
||||||
add2Mapping(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
|
//this is to do optimized lookup of permissions for a given collection/path
|
||||||
private void add2Mapping(Permission permission) {
|
private void add2Mapping(Permission permission) {
|
||||||
for (String c : permission.collections) {
|
for (String c : permission.collections) {
|
||||||
WildCardSupportMap m = mapping.get(c);
|
WildCardSupportMap m = mapping.computeIfAbsent(c, k -> new WildCardSupportMap());
|
||||||
if (m == null) mapping.put(c, m = new WildCardSupportMap());
|
|
||||||
for (String path : permission.path) {
|
for (String path : permission.path) {
|
||||||
List<Permission> perms = m.get(path);
|
List<Permission> perms = m.get(path);
|
||||||
if (perms == null) m.put(path, perms = new ArrayList<>());
|
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<String> getUserRoles(AuthorizationContext context) {
|
||||||
|
return getUserRoles(context.getUserPrincipal());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds users roles
|
* Finds users roles
|
||||||
* @param principal the user Principal to fetch roles for
|
* @param principal the user Principal to fetch roles for
|
||||||
|
@ -330,12 +337,11 @@ public abstract class RuleBasedAuthorizationPluginBase implements AuthorizationP
|
||||||
return latestConf;
|
return latestConf;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Map<String, AutorizationEditOperation> ops = unmodifiableMap(asList(AutorizationEditOperation.values()).stream().collect(toMap(AutorizationEditOperation::getOperationName, identity())));
|
private static final Map<String, AutorizationEditOperation> ops = Arrays.stream(AutorizationEditOperation.values()).collect(toMap(AutorizationEditOperation::getOperationName, identity()));
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidatingJsonMap getSpec() {
|
public ValidatingJsonMap getSpec() {
|
||||||
return Utils.getSpec("cluster.security.RuleBasedAuthorization").getSpec();
|
return Utils.getSpec("cluster.security.RuleBasedAuthorization").getSpec();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1133,6 +1133,11 @@ public class HttpSolrCall {
|
||||||
return getReq().getUserPrincipal();
|
return getReq().getUserPrincipal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUserName() {
|
||||||
|
return getReq().getRemoteUser();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getHttpHeader(String s) {
|
public String getHttpHeader(String s) {
|
||||||
return getReq().getHeader(s);
|
return getReq().getHeader(s);
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,10 +23,8 @@ import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.apache.http.auth.BasicUserPrincipal;
|
import org.apache.http.auth.BasicUserPrincipal;
|
||||||
import org.apache.solr.SolrTestCaseJ4;
|
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.request.SolrRequestHandler;
|
||||||
import org.apache.solr.security.AuthorizationContext.CollectionRequest;
|
import org.apache.solr.security.AuthorizationContext.CollectionRequest;
|
||||||
import org.apache.solr.security.AuthorizationContext.RequestType;
|
import org.apache.solr.security.AuthorizationContext.RequestType;
|
||||||
|
import org.apache.solr.util.LogLevel;
|
||||||
import org.hamcrest.core.IsInstanceOf;
|
import org.hamcrest.core.IsInstanceOf;
|
||||||
import org.hamcrest.core.IsNot;
|
import org.hamcrest.core.IsNot;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
|
@ -54,16 +54,18 @@ import static java.util.Collections.singletonList;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static org.apache.solr.common.util.CommandOperation.captureErrors;
|
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.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
|
* Base class for testing RBAC. This will test the {@link RuleBasedAuthorizationPlugin} implementation
|
||||||
* but also serves as a base class for testing other sub classes
|
* but also serves as a base class for testing other sub classes
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@LogLevel("org.apache.solr.security=TRACE")
|
||||||
public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
@SuppressWarnings({"rawtypes"})
|
protected Map<String, Object> rules;
|
||||||
protected Map rules;
|
|
||||||
|
|
||||||
final int STATUS_OK = 200;
|
final int STATUS_OK = 200;
|
||||||
final int FORBIDDEN = 403;
|
final int FORBIDDEN = 403;
|
||||||
|
@ -75,6 +77,11 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
resetPermissionsAndRoles();
|
resetPermissionsAndRoles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setupPermissionsAndRoles() {
|
||||||
|
resetPermissionsAndRoles();
|
||||||
|
}
|
||||||
|
|
||||||
protected void resetPermissionsAndRoles() {
|
protected void resetPermissionsAndRoles() {
|
||||||
String permissions = "{" +
|
String permissions = "{" +
|
||||||
" user-role : {" +
|
" user-role : {" +
|
||||||
|
@ -97,110 +104,101 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
" }," +
|
" }," +
|
||||||
"{name:read, role:dev }," +
|
"{name:read, role:dev }," +
|
||||||
"{name:freeforall, path:'/foo', role:'*'}]}";
|
"{name:freeforall, path:'/foo', role:'*'}]}";
|
||||||
rules = (Map) Utils.fromJSONString(permissions);
|
rules = (Map<String,Object>) Utils.fromJSONString(permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBasicPermissions() {
|
public void testBasicPermissions() {
|
||||||
checkRules(makeMap("resource", "/update/json/docs",
|
checkRules(Map.of("resource", "/update/json/docs",
|
||||||
"httpMethod", "POST",
|
"httpMethod", "POST",
|
||||||
"userPrincipal", "unknownuser",
|
"userPrincipal", "unknownuser",
|
||||||
"collectionRequests", "freeforall",
|
"collectionRequests", "freeforall",
|
||||||
"handler", new UpdateRequestHandler())
|
"handler", new UpdateRequestHandler())
|
||||||
, STATUS_OK);
|
, STATUS_OK);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/update/json/docs",
|
checkRules(Map.of("resource", "/update/json/docs",
|
||||||
"httpMethod", "POST",
|
"httpMethod", "POST",
|
||||||
"userPrincipal", "tim",
|
"userPrincipal", "tim",
|
||||||
"collectionRequests", "mycoll",
|
"collectionRequests", "mycoll",
|
||||||
"handler", new UpdateRequestHandler())
|
"handler", new UpdateRequestHandler())
|
||||||
, STATUS_OK);
|
, STATUS_OK);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/update/json/docs",
|
checkRules(Map.of("resource", "/update/json/docs",
|
||||||
"httpMethod", "POST",
|
"httpMethod", "POST",
|
||||||
"collectionRequests", "mycoll",
|
"collectionRequests", "mycoll",
|
||||||
"handler", new UpdateRequestHandler())
|
"handler", new UpdateRequestHandler())
|
||||||
, PROMPT_FOR_CREDENTIALS);
|
, PROMPT_FOR_CREDENTIALS);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/schema",
|
checkRules(Map.of("resource", "/schema",
|
||||||
"userPrincipal", "somebody",
|
"userPrincipal", "somebody",
|
||||||
"collectionRequests", "mycoll",
|
"collectionRequests", "mycoll",
|
||||||
"httpMethod", "POST",
|
"httpMethod", "POST",
|
||||||
"handler", new SchemaHandler())
|
"handler", new SchemaHandler())
|
||||||
, FORBIDDEN);
|
, FORBIDDEN);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/schema",
|
checkRules(Map.of("resource", "/schema",
|
||||||
"userPrincipal", "somebody",
|
"userPrincipal", "somebody",
|
||||||
"collectionRequests", "mycoll",
|
"collectionRequests", "mycoll",
|
||||||
"httpMethod", "GET",
|
"httpMethod", "GET",
|
||||||
"handler", new SchemaHandler())
|
"handler", new SchemaHandler())
|
||||||
, STATUS_OK);
|
, STATUS_OK);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/schema/fields",
|
checkRules(Map.of("resource", "/schema/fields",
|
||||||
"userPrincipal", "somebody",
|
"userPrincipal", "somebody",
|
||||||
"collectionRequests", "mycoll",
|
"collectionRequests", "mycoll",
|
||||||
"httpMethod", "GET",
|
"httpMethod", "GET",
|
||||||
"handler", new SchemaHandler())
|
"handler", new SchemaHandler())
|
||||||
, STATUS_OK);
|
, STATUS_OK);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/schema",
|
checkRules(Map.of("resource", "/schema",
|
||||||
"userPrincipal", "somebody",
|
"userPrincipal", "somebody",
|
||||||
"collectionRequests", "mycoll",
|
"collectionRequests", "mycoll",
|
||||||
"httpMethod", "POST",
|
"httpMethod", "POST",
|
||||||
"handler", new SchemaHandler())
|
"handler", new SchemaHandler())
|
||||||
, FORBIDDEN);
|
, FORBIDDEN);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/admin/collections",
|
checkRules(Map.of("resource", "/admin/collections",
|
||||||
"userPrincipal", "tim",
|
"userPrincipal", "tim",
|
||||||
"requestType", RequestType.ADMIN,
|
"requestType", RequestType.ADMIN,
|
||||||
"collectionRequests", null,
|
|
||||||
"httpMethod", "GET",
|
"httpMethod", "GET",
|
||||||
"handler", new CollectionsHandler(),
|
"handler", new CollectionsHandler(),
|
||||||
"params", new MapSolrParams(singletonMap("action", "LIST")))
|
"params", new MapSolrParams(singletonMap("action", "LIST")))
|
||||||
, STATUS_OK);
|
, STATUS_OK);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/admin/collections",
|
checkRules(Map.of("resource", "/admin/collections",
|
||||||
"userPrincipal", null,
|
|
||||||
"requestType", RequestType.ADMIN,
|
"requestType", RequestType.ADMIN,
|
||||||
"collectionRequests", null,
|
|
||||||
"httpMethod", "GET",
|
"httpMethod", "GET",
|
||||||
"handler", new CollectionsHandler(),
|
"handler", new CollectionsHandler(),
|
||||||
"params", new MapSolrParams(singletonMap("action", "LIST")))
|
"params", new MapSolrParams(singletonMap("action", "LIST")))
|
||||||
, STATUS_OK);
|
, STATUS_OK);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/admin/collections",
|
checkRules(Map.of("resource", "/admin/collections",
|
||||||
"userPrincipal", null,
|
|
||||||
"requestType", RequestType.ADMIN,
|
"requestType", RequestType.ADMIN,
|
||||||
"collectionRequests", null,
|
|
||||||
"handler", new CollectionsHandler(),
|
"handler", new CollectionsHandler(),
|
||||||
"params", new MapSolrParams(singletonMap("action", "CREATE")))
|
"params", new MapSolrParams(singletonMap("action", "CREATE")))
|
||||||
, PROMPT_FOR_CREDENTIALS);
|
, PROMPT_FOR_CREDENTIALS);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/admin/collections",
|
checkRules(Map.of("resource", "/admin/collections",
|
||||||
"userPrincipal", null,
|
|
||||||
"requestType", RequestType.ADMIN,
|
"requestType", RequestType.ADMIN,
|
||||||
"collectionRequests", null,
|
|
||||||
"handler", new CollectionsHandler(),
|
"handler", new CollectionsHandler(),
|
||||||
"params", new MapSolrParams(singletonMap("action", "RELOAD")))
|
"params", new MapSolrParams(singletonMap("action", "RELOAD")))
|
||||||
, PROMPT_FOR_CREDENTIALS);
|
, PROMPT_FOR_CREDENTIALS);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/admin/collections",
|
checkRules(Map.of("resource", "/admin/collections",
|
||||||
"userPrincipal", "somebody",
|
"userPrincipal", "somebody",
|
||||||
"requestType", RequestType.ADMIN,
|
"requestType", RequestType.ADMIN,
|
||||||
"collectionRequests", null,
|
|
||||||
"handler", new CollectionsHandler(),
|
"handler", new CollectionsHandler(),
|
||||||
"params", new MapSolrParams(singletonMap("action", "CREATE")))
|
"params", new MapSolrParams(singletonMap("action", "CREATE")))
|
||||||
, FORBIDDEN);
|
, FORBIDDEN);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/admin/collections",
|
checkRules(Map.of("resource", "/admin/collections",
|
||||||
"userPrincipal", "tim",
|
"userPrincipal", "tim",
|
||||||
"requestType", RequestType.ADMIN,
|
"requestType", RequestType.ADMIN,
|
||||||
"collectionRequests", null,
|
|
||||||
"handler", new CollectionsHandler(),
|
"handler", new CollectionsHandler(),
|
||||||
"params", new MapSolrParams(singletonMap("action", "CREATE")))
|
"params", new MapSolrParams(singletonMap("action", "CREATE")))
|
||||||
, STATUS_OK);
|
, STATUS_OK);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/select",
|
checkRules(Map.of("resource", "/select",
|
||||||
"httpMethod", "GET",
|
"httpMethod", "GET",
|
||||||
"handler", new SearchHandler(),
|
"handler", new SearchHandler(),
|
||||||
"collectionRequests", singletonList(new CollectionRequest("mycoll")),
|
"collectionRequests", singletonList(new CollectionRequest("mycoll")),
|
||||||
|
@ -210,94 +208,89 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
setUserRole("cio", "su");
|
setUserRole("cio", "su");
|
||||||
addPermission("all", "su");
|
addPermission("all", "su");
|
||||||
|
|
||||||
checkRules(makeMap("resource", ReplicationHandler.PATH,
|
checkRules(Map.of("resource", ReplicationHandler.PATH,
|
||||||
"httpMethod", "POST",
|
"httpMethod", "POST",
|
||||||
"userPrincipal", "tim",
|
"userPrincipal", "tim",
|
||||||
"handler", new ReplicationHandler(),
|
"handler", new ReplicationHandler(),
|
||||||
"collectionRequests", singletonList(new CollectionRequest("mycoll")) )
|
"collectionRequests", singletonList(new CollectionRequest("mycoll")) )
|
||||||
, FORBIDDEN);
|
, FORBIDDEN);
|
||||||
|
|
||||||
checkRules(makeMap("resource", ReplicationHandler.PATH,
|
checkRules(Map.of("resource", ReplicationHandler.PATH,
|
||||||
"httpMethod", "POST",
|
"httpMethod", "POST",
|
||||||
"userPrincipal", "cio",
|
"userPrincipal", "cio",
|
||||||
"handler", new ReplicationHandler(),
|
"handler", new ReplicationHandler(),
|
||||||
"collectionRequests", singletonList(new CollectionRequest("mycoll")) )
|
"collectionRequests", singletonList(new CollectionRequest("mycoll")) )
|
||||||
, STATUS_OK);
|
, STATUS_OK);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/admin/collections",
|
checkRules(Map.of("resource", "/admin/collections",
|
||||||
"userPrincipal", "tim",
|
"userPrincipal", "tim",
|
||||||
"requestType", AuthorizationContext.RequestType.ADMIN,
|
"requestType", AuthorizationContext.RequestType.ADMIN,
|
||||||
"collectionRequests", null,
|
|
||||||
"handler", new CollectionsHandler(),
|
"handler", new CollectionsHandler(),
|
||||||
"params", new MapSolrParams(singletonMap("action", "CREATE")))
|
"params", new MapSolrParams(singletonMap("action", "CREATE")))
|
||||||
, STATUS_OK);
|
, STATUS_OK);
|
||||||
|
}
|
||||||
|
|
||||||
resetPermissionsAndRoles();
|
@Test
|
||||||
|
public void testCoreAdminPermissions() {
|
||||||
addPermission("core-admin-edit", "su");
|
addPermission("core-admin-edit", "su");
|
||||||
addPermission("core-admin-read", "user");
|
addPermission("core-admin-read", "user");
|
||||||
setUserRole("cio", "su");
|
setUserRole("cio", "su");
|
||||||
addPermission("all", "su");
|
addPermission("all", "su");
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/admin/cores",
|
checkRules(Map.of("resource", "/admin/cores",
|
||||||
"userPrincipal", null,
|
|
||||||
"requestType", RequestType.ADMIN,
|
"requestType", RequestType.ADMIN,
|
||||||
"collectionRequests", null,
|
|
||||||
"handler", new CoreAdminHandler(null),
|
"handler", new CoreAdminHandler(null),
|
||||||
"params", new MapSolrParams(singletonMap("action", "CREATE")))
|
"params", new MapSolrParams(singletonMap("action", "CREATE")))
|
||||||
, PROMPT_FOR_CREDENTIALS);
|
, PROMPT_FOR_CREDENTIALS);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/admin/cores",
|
checkRules(Map.of("resource", "/admin/cores",
|
||||||
"userPrincipal", "joe",
|
"userPrincipal", "joe",
|
||||||
"requestType", RequestType.ADMIN,
|
"requestType", RequestType.ADMIN,
|
||||||
"collectionRequests", null,
|
|
||||||
"handler", new CoreAdminHandler(null),
|
"handler", new CoreAdminHandler(null),
|
||||||
"params", new MapSolrParams(singletonMap("action", "CREATE")))
|
"params", new MapSolrParams(singletonMap("action", "CREATE")))
|
||||||
, FORBIDDEN);
|
, FORBIDDEN);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/admin/cores",
|
checkRules(Map.of("resource", "/admin/cores",
|
||||||
"userPrincipal", "joe",
|
"userPrincipal", "joe",
|
||||||
"requestType", RequestType.ADMIN,
|
"requestType", RequestType.ADMIN,
|
||||||
"collectionRequests", null,
|
|
||||||
"handler", new CoreAdminHandler(null),
|
"handler", new CoreAdminHandler(null),
|
||||||
"params", new MapSolrParams(singletonMap("action", "STATUS")))
|
"params", new MapSolrParams(singletonMap("action", "STATUS")))
|
||||||
, STATUS_OK);
|
, STATUS_OK);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/admin/cores",
|
checkRules(Map.of("resource", "/admin/cores",
|
||||||
"userPrincipal", "cio",
|
"userPrincipal", "cio",
|
||||||
"requestType", RequestType.ADMIN,
|
"requestType", RequestType.ADMIN,
|
||||||
"collectionRequests", null,
|
|
||||||
"handler", new CoreAdminHandler(null),
|
"handler", new CoreAdminHandler(null),
|
||||||
"params", new MapSolrParams(singletonMap("action", "CREATE")))
|
"params", new MapSolrParams(singletonMap("action", "CREATE")))
|
||||||
,STATUS_OK);
|
,STATUS_OK);
|
||||||
|
}
|
||||||
|
|
||||||
resetPermissionsAndRoles();
|
@Test
|
||||||
addPermission("test-params", "admin", "/x", makeMap("key", Arrays.asList("REGEX:(?i)val1", "VAL2")));
|
public void testParamsPermissions() {
|
||||||
|
addPermission("test-params", "admin", "/x", Map.of("key", Arrays.asList("REGEX:(?i)val1", "VAL2")));
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/x",
|
checkRules(Map.of("resource", "/x",
|
||||||
"userPrincipal", null,
|
|
||||||
"requestType", RequestType.UNKNOWN,
|
"requestType", RequestType.UNKNOWN,
|
||||||
"collectionRequests", "go",
|
"collectionRequests", "go",
|
||||||
"handler", new DumpRequestHandler(),
|
"handler", new DumpRequestHandler(),
|
||||||
"params", new MapSolrParams(singletonMap("key", "VAL1")))
|
"params", new MapSolrParams(singletonMap("key", "VAL1")))
|
||||||
, PROMPT_FOR_CREDENTIALS);
|
, PROMPT_FOR_CREDENTIALS);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/x",
|
checkRules(Map.of("resource", "/x",
|
||||||
"userPrincipal", null,
|
|
||||||
"requestType", RequestType.UNKNOWN,
|
"requestType", RequestType.UNKNOWN,
|
||||||
"collectionRequests", "go",
|
"collectionRequests", "go",
|
||||||
"handler", new DumpRequestHandler(),
|
"handler", new DumpRequestHandler(),
|
||||||
"params", new MapSolrParams(singletonMap("key", "Val1")))
|
"params", new MapSolrParams(singletonMap("key", "Val1")))
|
||||||
, PROMPT_FOR_CREDENTIALS);
|
, PROMPT_FOR_CREDENTIALS);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/x",
|
checkRules(Map.of("resource", "/x",
|
||||||
"userPrincipal", null,
|
|
||||||
"requestType", RequestType.UNKNOWN,
|
"requestType", RequestType.UNKNOWN,
|
||||||
"collectionRequests", "go",
|
"collectionRequests", "go",
|
||||||
"handler", new DumpRequestHandler(),
|
"handler", new DumpRequestHandler(),
|
||||||
"params", new MapSolrParams(singletonMap("key", "Val1")))
|
"params", new MapSolrParams(singletonMap("key", "Val1")))
|
||||||
, PROMPT_FOR_CREDENTIALS);
|
, PROMPT_FOR_CREDENTIALS);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/x",
|
checkRules(Map.of("resource", "/x",
|
||||||
"userPrincipal", "joe",
|
"userPrincipal", "joe",
|
||||||
"requestType", RequestType.UNKNOWN,
|
"requestType", RequestType.UNKNOWN,
|
||||||
"collectionRequests", "go",
|
"collectionRequests", "go",
|
||||||
|
@ -305,7 +298,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
"params", new MapSolrParams(singletonMap("key", "Val1")))
|
"params", new MapSolrParams(singletonMap("key", "Val1")))
|
||||||
, FORBIDDEN);
|
, FORBIDDEN);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/x",
|
checkRules(Map.of("resource", "/x",
|
||||||
"userPrincipal", "joe",
|
"userPrincipal", "joe",
|
||||||
"requestType", RequestType.UNKNOWN,
|
"requestType", RequestType.UNKNOWN,
|
||||||
"collectionRequests", "go",
|
"collectionRequests", "go",
|
||||||
|
@ -313,26 +306,24 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
"params", new MapSolrParams(singletonMap("key", "Val2")))
|
"params", new MapSolrParams(singletonMap("key", "Val2")))
|
||||||
, STATUS_OK);
|
, STATUS_OK);
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/x",
|
checkRules(Map.of("resource", "/x",
|
||||||
"userPrincipal", "joe",
|
"userPrincipal", "joe",
|
||||||
"requestType", RequestType.UNKNOWN,
|
"requestType", RequestType.UNKNOWN,
|
||||||
"collectionRequests", "go",
|
"collectionRequests", "go",
|
||||||
"handler", new DumpRequestHandler(),
|
"handler", new DumpRequestHandler(),
|
||||||
"params", new MapSolrParams(singletonMap("key", "VAL2")))
|
"params", new MapSolrParams(singletonMap("key", "VAL2")))
|
||||||
, FORBIDDEN);
|
, FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCustomRules() {
|
||||||
Map<String, Object> customRules = (Map<String, Object>) Utils.fromJSONString(
|
Map<String, Object> customRules = (Map<String, Object>) Utils.fromJSONString(
|
||||||
"{permissions:[" +
|
"{permissions:[" +
|
||||||
" {name:update, role:[admin_role,update_role]}," +
|
" {name:update, role:[admin_role,update_role]}," +
|
||||||
" {name:read, role:[admin_role,update_role,read_role]}" +
|
" {name:read, role:[admin_role,update_role,read_role]}" +
|
||||||
"]}");
|
"]}");
|
||||||
|
|
||||||
clearUserRoles();
|
checkRules(Map.of("resource", "/update",
|
||||||
setUserRole("admin", "admin_role");
|
|
||||||
setUserRole("update", "update_role");
|
|
||||||
setUserRole("solr", "read_role");
|
|
||||||
|
|
||||||
checkRules(makeMap("resource", "/update",
|
|
||||||
"userPrincipal", "solr",
|
"userPrincipal", "solr",
|
||||||
"requestType", RequestType.UNKNOWN,
|
"requestType", RequestType.UNKNOWN,
|
||||||
"collectionRequests", "go",
|
"collectionRequests", "go",
|
||||||
|
@ -353,7 +344,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
setUserRole("dev", "dev");
|
setUserRole("dev", "dev");
|
||||||
setUserRole("admin", "admin");
|
setUserRole("admin", "admin");
|
||||||
addPermission("all", "dev", "admin");
|
addPermission("all", "dev", "admin");
|
||||||
checkRules(makeMap("resource", "/update",
|
checkRules(Map.of("resource", "/update",
|
||||||
"userPrincipal", "dev",
|
"userPrincipal", "dev",
|
||||||
"requestType", RequestType.UNKNOWN,
|
"requestType", RequestType.UNKNOWN,
|
||||||
"collectionRequests", "go",
|
"collectionRequests", "go",
|
||||||
|
@ -363,7 +354,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
|
|
||||||
handler = new PropertiesRequestHandler();
|
handler = new PropertiesRequestHandler();
|
||||||
assertThat(handler, new IsNot<>(new IsInstanceOf(PermissionNameProvider.class)));
|
assertThat(handler, new IsNot<>(new IsInstanceOf(PermissionNameProvider.class)));
|
||||||
checkRules(makeMap("resource", "/admin/info/properties",
|
checkRules(Map.of("resource", "/admin/info/properties",
|
||||||
"userPrincipal", "dev",
|
"userPrincipal", "dev",
|
||||||
"requestType", RequestType.UNKNOWN,
|
"requestType", RequestType.UNKNOWN,
|
||||||
"collectionRequests", "go",
|
"collectionRequests", "go",
|
||||||
|
@ -385,7 +376,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
setUserRole("dev", "dev");
|
setUserRole("dev", "dev");
|
||||||
setUserRole("admin", "admin");
|
setUserRole("admin", "admin");
|
||||||
addPermission("all", "*");
|
addPermission("all", "*");
|
||||||
checkRules(makeMap("resource", "/update",
|
checkRules(Map.of("resource", "/update",
|
||||||
"userPrincipal", "dev",
|
"userPrincipal", "dev",
|
||||||
"requestType", RequestType.UNKNOWN,
|
"requestType", RequestType.UNKNOWN,
|
||||||
"collectionRequests", "go",
|
"collectionRequests", "go",
|
||||||
|
@ -395,7 +386,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
|
|
||||||
handler = new PropertiesRequestHandler();
|
handler = new PropertiesRequestHandler();
|
||||||
assertThat(handler, new IsNot<>(new IsInstanceOf(PermissionNameProvider.class)));
|
assertThat(handler, new IsNot<>(new IsInstanceOf(PermissionNameProvider.class)));
|
||||||
checkRules(makeMap("resource", "/admin/info/properties",
|
checkRules(Map.of("resource", "/admin/info/properties",
|
||||||
"userPrincipal", "dev",
|
"userPrincipal", "dev",
|
||||||
"requestType", RequestType.UNKNOWN,
|
"requestType", RequestType.UNKNOWN,
|
||||||
"collectionRequests", "go",
|
"collectionRequests", "go",
|
||||||
|
@ -416,7 +407,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
setUserRole("dev", "dev");
|
setUserRole("dev", "dev");
|
||||||
setUserRole("admin", "admin");
|
setUserRole("admin", "admin");
|
||||||
addPermission("all", "admin");
|
addPermission("all", "admin");
|
||||||
checkRules(makeMap("resource", "/update",
|
checkRules(Map.of("resource", "/update",
|
||||||
"userPrincipal", "dev",
|
"userPrincipal", "dev",
|
||||||
"requestType", RequestType.UNKNOWN,
|
"requestType", RequestType.UNKNOWN,
|
||||||
"collectionRequests", "go",
|
"collectionRequests", "go",
|
||||||
|
@ -426,7 +417,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
|
|
||||||
handler = new PropertiesRequestHandler();
|
handler = new PropertiesRequestHandler();
|
||||||
assertThat(handler, new IsNot<>(new IsInstanceOf(PermissionNameProvider.class)));
|
assertThat(handler, new IsNot<>(new IsInstanceOf(PermissionNameProvider.class)));
|
||||||
checkRules(makeMap("resource", "/admin/info/properties",
|
checkRules(Map.of("resource", "/admin/info/properties",
|
||||||
"userPrincipal", "dev",
|
"userPrincipal", "dev",
|
||||||
"requestType", RequestType.UNKNOWN,
|
"requestType", RequestType.UNKNOWN,
|
||||||
"collectionRequests", "go",
|
"collectionRequests", "go",
|
||||||
|
@ -435,22 +426,36 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
, FORBIDDEN);
|
, FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addPermission(String permissionName, String role, String path, Map<String, Object> params) {
|
@Test
|
||||||
((List)rules.get("permissions")).add( makeMap("name", permissionName, "role", role, "path", path, "params", params));
|
public void testShortNameResolvesPermissions() {
|
||||||
|
assumeThat("ExternalRBAPlugin doesn't use short name",
|
||||||
|
createPlugin(), is(instanceOf(RuleBasedAuthorizationPlugin.class)));
|
||||||
|
|
||||||
|
setUserRole("admin", "admin");
|
||||||
|
addPermission("all", "admin");
|
||||||
|
|
||||||
|
Map<String, Object> 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) {
|
void addPermission(String permissionName, String role, String path, Map<String, Object> params) {
|
||||||
List<Map<String,Object>> oldPerm = ((List) rules.get("permissions"));
|
((List)rules.get("permissions")).add(Map.of("name", permissionName, "role", role, "path", path, "params", params));
|
||||||
List<Map<String, Object>> newPerm = oldPerm.stream().filter(p -> !p.get("name").equals(name)).collect(Collectors.toList());
|
|
||||||
rules.put("permissions", newPerm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addPermission(String permissionName, String... roles) {
|
protected void addPermission(String permissionName, String... roles) {
|
||||||
((List)rules.get("permissions")).add( makeMap("name", permissionName, "role", Arrays.asList(roles)));
|
((List)rules.get("permissions")).add(Map.of("name", permissionName, "role", Arrays.asList(roles)));
|
||||||
}
|
|
||||||
|
|
||||||
void clearUserRoles() {
|
|
||||||
rules.put("user-role", new HashMap<String,Object>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setUserRole(String user, String role) {
|
protected void setUserRole(String user, String role) {
|
||||||
|
@ -465,8 +470,7 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
assertEquals("admin", perms.getVal("permissions[0]/role"));
|
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:2 } }", false);
|
||||||
perms.runCmd("{set-permission : {name: config-edit, role: [admin, dev], index:1}}", true);
|
perms.runCmd("{set-permission : {name: config-edit, role: [admin, dev], index:1}}", true);
|
||||||
@SuppressWarnings({"rawtypes"})
|
Collection<String> roles = (Collection<String>) perms.getVal("permissions[0]/role");
|
||||||
Collection roles = (Collection) perms.getVal("permissions[0]/role");
|
|
||||||
assertEquals(2, roles.size());
|
assertEquals(2, roles.size());
|
||||||
assertTrue(roles.contains("admin"));
|
assertTrue(roles.contains("admin"));
|
||||||
assertTrue(roles.contains("dev"));
|
assertTrue(roles.contains("dev"));
|
||||||
|
@ -485,16 +489,13 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
assertEquals("y", perms.getVal("permissions[1]/collection"));
|
assertEquals("y", perms.getVal("permissions[1]/collection"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Perms {
|
static class Perms {
|
||||||
@SuppressWarnings({"rawtypes"})
|
Map<String, Object> conf = new HashMap<>();
|
||||||
Map conf = new HashMap<>();
|
|
||||||
ConfigEditablePlugin plugin = new RuleBasedAuthorizationPlugin();
|
ConfigEditablePlugin plugin = new RuleBasedAuthorizationPlugin();
|
||||||
List<CommandOperation> parsedCommands;
|
List<CommandOperation> parsedCommands;
|
||||||
|
|
||||||
public void runCmd(String cmds, boolean failOnError) throws IOException {
|
public void runCmd(String cmds, boolean failOnError) throws IOException {
|
||||||
parsedCommands = CommandOperation.parse(new StringReader(cmds));
|
parsedCommands = CommandOperation.parse(new StringReader(cmds));
|
||||||
@SuppressWarnings({"rawtypes"})
|
|
||||||
LinkedList ll = new LinkedList();
|
|
||||||
Map<String, Object> edited = plugin.edit(conf, parsedCommands);
|
Map<String, Object> edited = plugin.edit(conf, parsedCommands);
|
||||||
if(edited!= null) conf = edited;
|
if(edited!= null) conf = edited;
|
||||||
@SuppressWarnings({"rawtypes"})
|
@SuppressWarnings({"rawtypes"})
|
||||||
|
@ -536,10 +537,15 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
Object userPrincipal = values.get("userPrincipal");
|
Object userPrincipal = values.get("userPrincipal");
|
||||||
return userPrincipal == null ? null : new BasicUserPrincipal(String.valueOf(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<String,Object> values;
|
private final Map<String,Object> values;
|
||||||
|
|
||||||
public MockAuthorizationContext(Map<String, Object> values) {
|
public MockAuthorizationContext(Map<String, Object> values) {
|
||||||
|
@ -552,14 +558,19 @@ public class BaseTestRuleBasedAuthorizationPlugin extends SolrTestCaseJ4 {
|
||||||
return params == null ? new MapSolrParams(new HashMap<>()) : params;
|
return params == null ? new MapSolrParams(new HashMap<>()) : params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUserName() {
|
||||||
|
Principal userPrincipal = getUserPrincipal();
|
||||||
|
return userPrincipal == null ? null : userPrincipal.getName();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getHttpHeader(String header) {
|
public String getHttpHeader(String header) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings({"rawtypes"})
|
public Enumeration<String> getHeaderNames() {
|
||||||
public Enumeration getHeaderNames() {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,10 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.solr.security.hadoop;
|
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.SolrQuery;
|
||||||
import org.apache.solr.client.solrj.impl.CloudSolrClient;
|
import org.apache.solr.client.solrj.impl.CloudSolrClient;
|
||||||
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
|
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
|
||||||
|
@ -43,7 +39,7 @@ public class TestSolrCloudWithHadoopAuthPlugin extends SolrCloudAuthTestCase {
|
||||||
public static void setupClass() throws Exception {
|
public static void setupClass() throws Exception {
|
||||||
HdfsTestUtil.checkAssumptions();
|
HdfsTestUtil.checkAssumptions();
|
||||||
|
|
||||||
setupMiniKdc();
|
kerberosTestServices = KerberosUtils.setupMiniKdc(createTempDir());
|
||||||
|
|
||||||
configureCluster(NUM_SERVERS)// nodes
|
configureCluster(NUM_SERVERS)// nodes
|
||||||
.withSecurityJson(TEST_PATH().resolve("security").resolve("hadoop_kerberos_config.json"))
|
.withSecurityJson(TEST_PATH().resolve("security").resolve("hadoop_kerberos_config.json"))
|
||||||
|
@ -54,55 +50,10 @@ public class TestSolrCloudWithHadoopAuthPlugin extends SolrCloudAuthTestCase {
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void tearDownClass() throws Exception {
|
public static void tearDownClass() throws Exception {
|
||||||
System.clearProperty("java.security.auth.login.config");
|
KerberosUtils.cleanupMiniKdc(kerberosTestServices);
|
||||||
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();
|
|
||||||
}
|
|
||||||
kerberosTestServices = null;
|
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
|
@Test
|
||||||
public void testBasics() throws Exception {
|
public void testBasics() throws Exception {
|
||||||
testCollectionCreateSearchDelete();
|
testCollectionCreateSearchDelete();
|
||||||
|
|
|
@ -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'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:
|
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]
|
[source,json]
|
||||||
----
|
----
|
||||||
"user-role": {
|
"user-role": {
|
||||||
|
@ -82,6 +81,11 @@ user-role:: A mapping of individual users to the roles they belong to. The valu
|
||||||
"user2": ["role1", "role2"]
|
"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
|
=== Example for RuleBasedAuthorizationPlugin and BasicAuth
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue