This is an automated email from the ASF dual-hosted git repository.
spolavarapu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new 8ed1b33 RANGER-2393: Document level authorization support for solr
8ed1b33 is described below
commit 8ed1b3385abfdb1e725f10d131bee7d339bb8fd4
Author: Sailaja Polavarapu <[email protected]>
AuthorDate: Thu Sep 19 15:18:48 2019 -0700
RANGER-2393: Document level authorization support for solr
---
.../solr/authorizer/RangerSolrAuthorizer.java | 193 +++++++++++++++++++--
.../solr/authorizer/RangerSolrAuthorizer.java | 92 +++++++++-
.../java/org/apache/ranger/biz/RoleDBStore.java | 22 ++-
.../java/org/apache/ranger/db/XXServiceDefDao.java | 12 ++
.../main/resources/META-INF/jpa_named_queries.xml | 4 +
5 files changed, 308 insertions(+), 15 deletions(-)
diff --git
a/plugin-solr/src/main/java/org/apache/ranger/authorization/solr/authorizer/RangerSolrAuthorizer.java
b/plugin-solr/src/main/java/org/apache/ranger/authorization/solr/authorizer/RangerSolrAuthorizer.java
index 48d4fb7..46d0f66 100644
---
a/plugin-solr/src/main/java/org/apache/ranger/authorization/solr/authorizer/RangerSolrAuthorizer.java
+++
b/plugin-solr/src/main/java/org/apache/ranger/authorization/solr/authorizer/RangerSolrAuthorizer.java
@@ -28,6 +28,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import com.google.common.base.Joiner;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -38,16 +39,40 @@ import
org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl;
import org.apache.ranger.plugin.policyengine.RangerAccessResult;
import org.apache.ranger.plugin.service.RangerBasePlugin;
import org.apache.ranger.plugin.util.RangerPerfTracer;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.handler.component.ResponseBuilder;
+import org.apache.solr.handler.component.SearchComponent;
+import org.apache.solr.request.LocalSolrQueryRequest;
+import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.security.AuthorizationContext.RequestType;
import org.apache.solr.security.AuthorizationPlugin;
import org.apache.solr.security.AuthorizationResponse;
import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.security.AuthorizationContext.CollectionRequest;
-public class RangerSolrAuthorizer implements AuthorizationPlugin {
+import javax.servlet.http.HttpServletRequest;
+
+public class RangerSolrAuthorizer extends SearchComponent implements
AuthorizationPlugin {
private static final Log logger = LogFactory
.getLog(RangerSolrAuthorizer.class);
private static final Log PERF_SOLRAUTH_REQUEST_LOG =
RangerPerfTracer.getPerfLogger("solrauth.request");
+ public static final String SUPERUSER =
System.getProperty("solr.authorization.superuser", "solr");
+
+ public static final String AUTH_FIELD_PROP = "rangerAuthField";
+ public static final String DEFAULT_AUTH_FIELD = "ranger_auth";
+ public static final String ALL_ROLES_TOKEN_PROP = "allRolesToken";
+ public static final String ENABLED_PROP = "enabled";
+ public static final String MODE_PROP = "matchMode";
+ public static final String DEFAULT_MODE_PROP =
MatchType.DISJUNCTIVE.toString();
+
+ public static final String ALLOW_MISSING_VAL_PROP = "allow_missing_val";
+ public static final String TOKEN_COUNT_PROP = "tokenCountField";
+ public static final String DEFAULT_TOKEN_COUNT_FIELD_PROP =
"ranger_auth_count";
+ public static final String QPARSER_PROP = "qParser";
public static final String PROP_USE_PROXY_IP =
"xasecure.solr.use_proxy_ip";
public static final String PROP_PROXY_IP_HEADER =
"xasecure.solr.proxy_ip_header";
@@ -69,9 +94,39 @@ public class RangerSolrAuthorizer implements
AuthorizationPlugin {
String proxyIPHeader = "HTTP_X_FORWARDED_FOR";
String solrAppName = "Client";
+ private String authField;
+ private String allRolesToken;
+ private boolean enabled;
+ private MatchType matchMode;
+ private String tokenCountField;
+ private boolean allowMissingValue;
+ private String qParserName;
+
+ private enum MatchType {
+ DISJUNCTIVE,
+ CONJUNCTIVE
+ }
+
public RangerSolrAuthorizer() {
logger.info("RangerSolrAuthorizer()");
+ }
+ @Override
+ public void init(NamedList args) {
+ SolrParams params = args.toSolrParams();
+ this.authField = params.get(AUTH_FIELD_PROP,
DEFAULT_AUTH_FIELD);
+ this.allRolesToken = params.get(ALL_ROLES_TOKEN_PROP, "");
+ this.enabled = params.getBool(ENABLED_PROP, false);
+ this.matchMode = MatchType.valueOf(params.get(MODE_PROP,
DEFAULT_MODE_PROP).toUpperCase());
+
+ if (this.matchMode == MatchType.CONJUNCTIVE) {
+ this.qParserName = params.get(QPARSER_PROP,
"subset").trim();
+ this.allowMissingValue =
params.getBool(ALLOW_MISSING_VAL_PROP, false);
+ this.tokenCountField = params.get(TOKEN_COUNT_PROP,
DEFAULT_TOKEN_COUNT_FIELD_PROP);
+ }
+ logger.info("RangerSolrAuthorizer.init(): authField={" +
authField + "}, allRolesToken={" + allRolesToken +
+ "}, enabled={" + enabled + "}, matchType={" +
matchMode + "}, qParserName={" + qParserName +
+ "}, allowMissingValue={" + allowMissingValue +
"}, tokenCountField={" + tokenCountField + "}");
}
/*
@@ -125,15 +180,6 @@ public class RangerSolrAuthorizer implements
AuthorizationPlugin {
}
}
- private void authToJAASFile() {
- try {
- MiscUtil.setUGIFromJAASConfig(solrAppName);
- logger.info("LoginUser=" + MiscUtil.getUGILoginUser());
- } catch (Throwable t) {
- logger.error("Error authenticating for appName=" +
solrAppName, t);
- }
- }
-
/*
* (non-Javadoc)
*
@@ -265,6 +311,56 @@ public class RangerSolrAuthorizer implements
AuthorizationPlugin {
return response;
}
+ @Override
+ public void prepare(ResponseBuilder rb) throws IOException {
+ if (!enabled) {
+ return;
+ }
+
+ String userName = getUserName(rb.req);
+ if (SUPERUSER.equals(userName)) {
+ return;
+ }
+
+ Set<String> roles = getRolesForUser(userName);
+ if (roles != null && !roles.isEmpty()) {
+ String filterQuery;
+ if (matchMode == MatchType.DISJUNCTIVE) {
+ filterQuery =
getDisjunctiveFilterQueryStr(roles);
+ } else {
+ filterQuery =
getConjunctiveFilterQueryStr(roles);
+ }
+ ModifiableSolrParams newParams = new
ModifiableSolrParams(rb.req.getParams());
+ newParams.add("fq", filterQuery);
+ rb.req.setParams(newParams);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Adding filter query {" +
filterQuery + "} for user {" + userName + "} with roles {" + roles + "}");
+ }
+
+ } else {
+ throw new
SolrException(SolrException.ErrorCode.UNAUTHORIZED,
+ "Request from user: " + userName + "
rejected because user is not associated with any roles");
+ }
+ }
+
+ @Override
+ public void process(ResponseBuilder rb) throws IOException {
+ }
+
+ @Override
+ public String getDescription() {
+ return "Handle Query Document Authorization";
+ }
+
+ private void authToJAASFile() {
+ try {
+ MiscUtil.setUGIFromJAASConfig(solrAppName);
+ logger.info("LoginUser=" + MiscUtil.getUGILoginUser());
+ } catch (Throwable t) {
+ logger.error("Error authenticating for appName=" +
solrAppName, t);
+ }
+ }
+
/**
* @param context
*/
@@ -424,4 +520,81 @@ public class RangerSolrAuthorizer implements
AuthorizationPlugin {
return accessType;
}
+ private void addDisjunctiveRawClause(StringBuilder builder, String
value) {
+ // requires a space before the first term, so the
+ // default lucene query parser will be used
+ builder.append(" {!raw f=").append(authField).append(" v=")
+ .append(value).append("}");
+ }
+
+ private String getDisjunctiveFilterQueryStr(Set<String> roles) {
+ if (roles != null && !roles.isEmpty()) {
+ StringBuilder builder = new StringBuilder();
+ for (String role : roles) {
+ addDisjunctiveRawClause(builder, role);
+ }
+ if (allRolesToken != null && !allRolesToken.isEmpty()) {
+ addDisjunctiveRawClause(builder, allRolesToken);
+ }
+ return builder.toString();
+ }
+ return null;
+ }
+
+ private String getConjunctiveFilterQueryStr(Set<String> roles) {
+ StringBuilder filterQuery = new StringBuilder();
+ filterQuery
+ .append(" {!").append(qParserName)
+ .append("
set_field=\"").append(authField).append("\"")
+ .append("
set_value=\"").append(Joiner.on(',').join(roles.iterator())).append("\"")
+ .append("
count_field=\"").append(tokenCountField).append("\"");
+ if (allRolesToken != null && !allRolesToken.equals("")) {
+ filterQuery.append("
wildcard_token=\"").append(allRolesToken).append("\"");
+ }
+ filterQuery.append("
allow_missing_val=").append(allowMissingValue).append(" }");
+
+ return filterQuery.toString();
+ }
+
+ private Set<String> getRolesForUser(String name) {
+ if (solrPlugin.getCurrentRangerAuthContext() != null) {
+ return
solrPlugin.getCurrentRangerAuthContext().getRolesFromUserAndGroups(name,
getGroupsForUser(name));
+ }
+ else {
+ logger.info("Current Ranger Auth Context is null!!");
+ return null;
+ }
+ }
+
+ /**
+ * This method return the user name from the provided {@linkplain
SolrQueryRequest}
+ */
+ private final String getUserName(SolrQueryRequest req) {
+ // If a local request, treat it like a super user request; i.e.
it is equivalent to an
+ // http request from the same process.
+ if (req instanceof LocalSolrQueryRequest) {
+ return SUPERUSER;
+ }
+
+ SolrCore solrCore = req.getCore();
+
+ HttpServletRequest httpServletRequest = (HttpServletRequest)
req.getContext().get("httpRequest");
+ if (httpServletRequest == null) {
+ StringBuilder builder = new StringBuilder("Unable to
locate HttpServletRequest");
+ if (solrCore != null &&
!solrCore.getSolrConfig().getBool("requestDispatcher/requestParsers/@addHttpRequestToContext",
true)) {
+ builder.append(", ensure
requestDispatcher/requestParsers/@addHttpRequestToContext is set to true in
solrconfig.xml");
+ }
+ throw new
SolrException(SolrException.ErrorCode.UNAUTHORIZED, builder.toString());
+ }
+
+ String userName = httpServletRequest.getRemoteUser();
+ if (userName == null) {
+ userName =
MiscUtil.getShortNameFromPrincipalName(httpServletRequest.getUserPrincipal().getName());
+ }
+ if (userName == null) {
+ throw new
SolrException(SolrException.ErrorCode.UNAUTHORIZED, "This request is not
authenticated.");
+ }
+
+ return userName;
+ }
}
diff --git
a/ranger-solr-plugin-shim/src/main/java/org/apache/ranger/authorization/solr/authorizer/RangerSolrAuthorizer.java
b/ranger-solr-plugin-shim/src/main/java/org/apache/ranger/authorization/solr/authorizer/RangerSolrAuthorizer.java
index 4cfa7e1..cfede46 100644
---
a/ranger-solr-plugin-shim/src/main/java/org/apache/ranger/authorization/solr/authorizer/RangerSolrAuthorizer.java
+++
b/ranger-solr-plugin-shim/src/main/java/org/apache/ranger/authorization/solr/authorizer/RangerSolrAuthorizer.java
@@ -26,11 +26,14 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ranger.plugin.classloader.RangerPluginClassLoader;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.handler.component.ResponseBuilder;
+import org.apache.solr.handler.component.SearchComponent;
import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.security.AuthorizationPlugin;
import org.apache.solr.security.AuthorizationResponse;
-public class RangerSolrAuthorizer implements AuthorizationPlugin {
+public class RangerSolrAuthorizer extends SearchComponent implements
AuthorizationPlugin {
private static final Log LOG = LogFactory
.getLog(RangerSolrAuthorizer.class);
@@ -38,6 +41,7 @@ public class RangerSolrAuthorizer implements
AuthorizationPlugin {
private static final String RANGER_SOLR_AUTHORIZER_IMPL_CLASSNAME =
"org.apache.ranger.authorization.solr.authorizer.RangerSolrAuthorizer";
private AuthorizationPlugin
rangerSolrAuthorizerImpl = null;
+ private SearchComponent
rangerSearchComponentImpl = null;
private static RangerPluginClassLoader rangerPluginClassLoader
= null;
public RangerSolrAuthorizer() {
@@ -65,7 +69,9 @@ public class RangerSolrAuthorizer implements
AuthorizationPlugin {
activatePluginClassLoader();
- rangerSolrAuthorizerImpl = cls.newInstance();
+ Object impl = cls.newInstance();
+ rangerSolrAuthorizerImpl = (AuthorizationPlugin)impl;
+ rangerSearchComponentImpl = (SearchComponent)impl;
} catch (Exception e) {
// check what need to be done
LOG.error("Error Enabling RangerSolrPlugin", e);
@@ -98,6 +104,24 @@ public class RangerSolrAuthorizer implements
AuthorizationPlugin {
}
@Override
+ public void init(NamedList args) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("==> RangerSolrAuthorizer.init(" +
args.toString() + ")");
+ }
+ try {
+ activatePluginClassLoader();
+
+ rangerSearchComponentImpl.init(args);
+ } finally {
+ deactivatePluginClassLoader();
+ }
+
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("<== RangerSolrAuthorizer.init(" +
args.toString() + ")");
+ }
+ }
+
+ @Override
public void close() throws IOException {
if(LOG.isDebugEnabled()) {
LOG.debug("==> RangerSolrAuthorizer.close(Resource)");
@@ -119,7 +143,7 @@ public class RangerSolrAuthorizer implements
AuthorizationPlugin {
@Override
public AuthorizationResponse authorize(AuthorizationContext context) {
if(LOG.isDebugEnabled()) {
- LOG.debug("==> RangerSolrAuthorizer.init(context)");
+ LOG.debug("==>
RangerSolrAuthorizer.authorize(context)");
}
AuthorizationResponse ret = null;
try {
@@ -131,12 +155,72 @@ public class RangerSolrAuthorizer implements
AuthorizationPlugin {
}
if(LOG.isDebugEnabled()) {
- LOG.debug("<== RangerSolrAuthorizer.init(context)");
+ LOG.debug("<==
RangerSolrAuthorizer.authorize(context)");
}
return ret;
}
+ @Override
+ public void prepare(ResponseBuilder responseBuilder) throws IOException
{
+
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("==> RangerSolrAuthorizer.prepare()");
+ }
+
+ try {
+ activatePluginClassLoader();
+
+ rangerSearchComponentImpl.prepare(responseBuilder);
+ } finally {
+ deactivatePluginClassLoader();
+ }
+
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("<== RangerSolrAuthorizer.prepare()");
+ }
+ }
+
+ @Override
+ public void process(ResponseBuilder responseBuilder) throws IOException
{
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("==> RangerSolrAuthorizer.process()");
+ }
+ try {
+ activatePluginClassLoader();
+
+ rangerSearchComponentImpl.process(responseBuilder);
+ } finally {
+ deactivatePluginClassLoader();
+ }
+
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("<== RangerSolrAuthorizer.process()");
+ }
+
+ }
+
+ @Override
+ public String getDescription() {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("==> RangerSolrAuthorizer.getDescription()");
+ }
+
+ String ret = null;
+ try {
+ activatePluginClassLoader();
+
+ ret = rangerSearchComponentImpl.getDescription();
+ } finally {
+ deactivatePluginClassLoader();
+ }
+
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("<== RangerSolrAuthorizer.getDescription()");
+ }
+ return ret;
+ }
+
private void activatePluginClassLoader() {
if(rangerPluginClassLoader != null) {
rangerPluginClassLoader.activate();
diff --git
a/security-admin/src/main/java/org/apache/ranger/biz/RoleDBStore.java
b/security-admin/src/main/java/org/apache/ranger/biz/RoleDBStore.java
index 28b2c11..7a67e9c 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/RoleDBStore.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/RoleDBStore.java
@@ -29,6 +29,7 @@ import org.apache.commons.collections.ListUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.authorization.hadoop.config.RangerConfiguration;
import org.apache.ranger.common.MessageEnums;
import org.apache.ranger.common.RESTErrorUtil;
import org.apache.ranger.db.RangerDaoManager;
@@ -226,8 +227,27 @@ public class RoleDBStore implements RoleStore {
public List<RangerRole> getRoles(Long serviceId) {
List<RangerRole> ret = ListUtils.EMPTY_LIST;
+
if (serviceId != null) {
- List<XXRole> rolesFromDb =
daoMgr.getXXRole().findByServiceId(serviceId);
+ String serviceTypeName =
daoMgr.getXXServiceDef().findServiceDefTypeByServiceId(serviceId);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Service Type for serviceId (" + serviceId + ") = "
+ serviceTypeName);
+ }
+ String serviceTypesToGetAllRoles =
RangerConfiguration.getInstance().get("ranger.admin.service.types.for.returning.all.roles",
"solr");
+
+ boolean getAllRoles = false;
+ if (StringUtils.isNotEmpty(serviceTypesToGetAllRoles)) {
+ String[] allRolesServiceTypes =
StringUtils.split(serviceTypesToGetAllRoles, ",");
+ if (allRolesServiceTypes != null) {
+ for (String allRolesServiceType : allRolesServiceTypes) {
+ if (StringUtils.equalsIgnoreCase(serviceTypeName,
allRolesServiceType)) {
+ getAllRoles = true;
+ break;
+ }
+ }
+ }
+ }
+ List<XXRole> rolesFromDb = getAllRoles ?
daoMgr.getXXRole().getAll() : daoMgr.getXXRole().findByServiceId(serviceId);
if (CollectionUtils.isNotEmpty(rolesFromDb)) {
ret = new ArrayList<>();
for (XXRole xxRole : rolesFromDb) {
diff --git
a/security-admin/src/main/java/org/apache/ranger/db/XXServiceDefDao.java
b/security-admin/src/main/java/org/apache/ranger/db/XXServiceDefDao.java
index 835e5fe..beef5bf 100644
--- a/security-admin/src/main/java/org/apache/ranger/db/XXServiceDefDao.java
+++ b/security-admin/src/main/java/org/apache/ranger/db/XXServiceDefDao.java
@@ -78,4 +78,16 @@ public class XXServiceDefDao extends BaseDao<XXServiceDef> {
}
return serviceType;
}
+
+ public String findServiceDefTypeByServiceId(Long serviceId) {
+ String serviceType = null;
+ try {
+ serviceType = getEntityManager()
+
.createNamedQuery("XXServiceDef.findServiceDefNameByServiceId", String.class)
+ .setParameter("id",
serviceId).getSingleResult();
+ } catch (NoResultException e) {
+ return null;
+ }
+ return serviceType;
+ }
}
\ No newline at end of file
diff --git a/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
b/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
index 3c3d1de..078588a 100755
--- a/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
+++ b/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
@@ -325,6 +325,10 @@
<query>select obj.name from XXServiceDef obj, XXService
serviceObj where obj.id = serviceObj.type and serviceObj.name = :name</query>
</named-query>
+ <named-query name="XXServiceDef.findServiceDefNameByServiceId">
+ <query>select obj.name from XXServiceDef obj, XXService
serviceObj where obj.id = serviceObj.type and serviceObj.id = :id</query>
+ </named-query>
+
<!-- XXResourceDef -->
<named-query name="XXResourceDef.findByNameAndDefId">
<query>select obj from XXResourceDef obj where obj.name = :name
and obj.defId = :defId order by obj.level</query>