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>

Reply via email to