You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by rm...@apache.org on 2019/09/27 20:09:25 UTC

[ranger] branch master updated: RANGER-2512:RangerRolesRESTClient for serving user group roles to the plugins for evaluation

This is an automated email from the ASF dual-hosted git repository.

rmani 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 b69227e  RANGER-2512:RangerRolesRESTClient for serving user group roles to the plugins for evaluation
b69227e is described below

commit b69227eb2f6b8aacb539a6454c714923257de059
Author: rmani <rm...@hortonworks.com>
AuthorDate: Thu Sep 26 23:27:31 2019 -0700

    RANGER-2512:RangerRolesRESTClient for serving user group roles to the plugins for evaluation
    
    Signed-off-by: rmani <rm...@hortonworks.com>
---
 .../admin/client/AbstractRangerAdminClient.java    |   5 +
 .../ranger/admin/client/RangerAdminClient.java     |   3 +
 .../ranger/admin/client/RangerAdminRESTClient.java |  81 +++++
 .../ranger/plugin/model/RangerPluginInfo.java      |  49 +++
 .../ranger/plugin/model/RangerPolicyDelta.java     |   3 +-
 .../plugin/policyengine/RangerPolicyEngine.java    |   7 +-
 .../policyengine/RangerPolicyEngineCache.java      |   4 +-
 .../policyengine/RangerPolicyEngineImpl.java       |  68 +++-
 .../ranger/plugin/service/RangerAuthContext.java   |  14 +-
 .../ranger/plugin/service/RangerBasePlugin.java    |  30 +-
 .../apache/ranger/plugin/util/PolicyRefresher.java |  20 +-
 .../apache/ranger/plugin/util/RangerRESTUtils.java |  20 ++
 .../org/apache/ranger/plugin/util/RangerRoles.java |  78 +++++
 .../ranger/plugin/util/RangerRolesProvider.java    | 352 +++++++++++++++++++++
 .../apache/ranger/plugin/util/RangerRolesUtil.java | 106 +++++++
 .../apache/ranger/plugin/util/ServicePolicies.java |  15 +-
 .../plugin/policyengine/TestPolicyEngine.java      |  48 ++-
 .../admin/client/RangerAdminJersey2RESTClient.java |  87 +++++
 .../optimized/current/ranger_core_db_mysql.sql     |   2 +
 .../043-add-role-version-in-serviceVersionInfo.sql |  34 ++
 .../optimized/current/ranger_core_db_oracle.sql    |   2 +
 .../043-add-role-version-in-serviceVersionInfo.sql |  28 ++
 .../optimized/current/ranger_core_db_postgres.sql  |   2 +
 .../043-add-role-version-in-serviceVersionInfo.sql |  36 +++
 .../current/ranger_core_db_sqlanywhere.sql         |   2 +
 .../043-add-role-version-in-serviceVersionInfo.sql |  25 ++
 .../optimized/current/ranger_core_db_sqlserver.sql |   2 +
 .../042-add-role-version-in-serviceVersionInfo.sql |  29 ++
 .../main/java/org/apache/ranger/biz/AssetMgr.java  | 123 +++++--
 .../java/org/apache/ranger/biz/RoleDBStore.java    |  39 +++
 .../java/org/apache/ranger/biz/ServiceDBStore.java |  69 +---
 .../org/apache/ranger/common/RangerRoleCache.java  | 142 +++++++++
 .../org/apache/ranger/db/XXGlobalStateDao.java     |  47 +++
 .../apache/ranger/entity/XXServiceVersionInfo.java |  22 ++
 .../main/java/org/apache/ranger/rest/RoleREST.java | 189 +++++++++++
 .../java/org/apache/ranger/rest/ServiceREST.java   |   2 -
 .../apache/ranger/service/RangerRoleService.java   |  43 +++
 37 files changed, 1688 insertions(+), 140 deletions(-)

diff --git a/agents-common/src/main/java/org/apache/ranger/admin/client/AbstractRangerAdminClient.java b/agents-common/src/main/java/org/apache/ranger/admin/client/AbstractRangerAdminClient.java
index 6367235..2bc7557 100644
--- a/agents-common/src/main/java/org/apache/ranger/admin/client/AbstractRangerAdminClient.java
+++ b/agents-common/src/main/java/org/apache/ranger/admin/client/AbstractRangerAdminClient.java
@@ -37,6 +37,11 @@ public abstract class AbstractRangerAdminClient implements RangerAdminClient {
     }
 
     @Override
+    public RangerRoles getRolesIfUpdated(long lastKnownRoleVersion, long lastActivationTimeInMillis) throws Exception {
+        return null;
+    }
+
+    @Override
     public RangerRole createRole(RangerRole request) throws Exception {
         return null;
     }
diff --git a/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminClient.java b/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminClient.java
index b09a9be..9510888 100644
--- a/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminClient.java
+++ b/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminClient.java
@@ -23,6 +23,7 @@
 import org.apache.ranger.plugin.model.RangerRole;
 import org.apache.ranger.plugin.util.GrantRevokeRequest;
 import org.apache.ranger.plugin.util.GrantRevokeRoleRequest;
+import org.apache.ranger.plugin.util.RangerRoles;
 import org.apache.ranger.plugin.util.ServicePolicies;
 import org.apache.ranger.plugin.util.ServiceTags;
 
@@ -35,6 +36,8 @@ public interface RangerAdminClient {
 
 	ServicePolicies getServicePoliciesIfUpdated(long lastKnownVersion, long lastActivationTimeInMillis) throws Exception;
 
+	RangerRoles getRolesIfUpdated(long lastKnownRoleVersion, long lastActivationTimeInMills) throws Exception;
+
 	RangerRole createRole(RangerRole request) throws Exception;
 
 	void dropRole(String execUser, String roleName) throws Exception;
diff --git a/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminRESTClient.java b/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminRESTClient.java
index 5939f38..f564ba5 100644
--- a/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminRESTClient.java
+++ b/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminRESTClient.java
@@ -195,6 +195,87 @@ public class RangerAdminRESTClient extends AbstractRangerAdminClient {
 	}
 
 	@Override
+	public RangerRoles getRolesIfUpdated(final long lastKnownRoleVersion, final long lastActivationTimeInMillis) throws Exception {
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerAdminRESTClient.getRolesIfUpdated(" + lastKnownRoleVersion + ", " + lastActivationTimeInMillis + ")");
+		}
+
+		final RangerRoles ret;
+		final UserGroupInformation user = MiscUtil.getUGILoginUser();
+		final boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled();
+		final ClientResponse response;
+
+		Map<String, String> queryParams = new HashMap<String, String>();
+		queryParams.put(RangerRESTUtils.REST_PARAM_LAST_KNOWN_ROLE_VERSION, Long.toString(lastKnownRoleVersion));
+		queryParams.put(RangerRESTUtils.REST_PARAM_LAST_ACTIVATION_TIME, Long.toString(lastActivationTimeInMillis));
+		queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, pluginId);
+		queryParams.put(RangerRESTUtils.REST_PARAM_CLUSTER_NAME, clusterName);
+
+		if (isSecureMode) {
+			if (LOG.isDebugEnabled()) {
+				LOG.debug("Checking Roles updated as user : " + user);
+			}
+			PrivilegedAction<ClientResponse> action = new PrivilegedAction<ClientResponse>() {
+				public ClientResponse run() {
+					ClientResponse clientRes = null;
+					String relativeURL = RangerRESTUtils.REST_URL_SERVICE_SERCURE_GET_USER_GROUP_ROLES + serviceNameUrlParam;
+					try {
+						clientRes =  restClient.get(relativeURL, queryParams);
+					} catch (Exception e) {
+						LOG.error("Failed to get response, Error is : "+e.getMessage());
+					}
+					return clientRes;
+				}
+			};
+			response = user.doAs(action);
+		} else {
+			if (LOG.isDebugEnabled()) {
+				LOG.debug("Checking Roles updated as user : " + user);
+			}
+			String relativeURL = RangerRESTUtils.REST_URL_SERVICE_GET_USER_GROUP_ROLES + serviceNameUrlParam;
+			response = restClient.get(relativeURL, queryParams);
+		}
+
+		if (response == null || response.getStatus() == HttpServletResponse.SC_NOT_MODIFIED) {
+			if (response == null) {
+				LOG.error("Error getting Roles; Received NULL response!!. secureMode=" + isSecureMode + ", user=" + user + ", serviceName=" + serviceName);
+			} else {
+				RESTResponse resp = RESTResponse.fromClientResponse(response);
+				if (LOG.isDebugEnabled()) {
+					LOG.debug("No change in Roles. secureMode=" + isSecureMode + ", user=" + user
+							+ ", response=" + resp + ", serviceName=" + serviceName
+							+ ", " + "lastKnownRoleVersion=" + lastKnownRoleVersion
+							+ ", " + "lastActivationTimeInMillis=" + lastActivationTimeInMillis);
+				}
+			}
+			ret = null;
+		} else if (response.getStatus() == HttpServletResponse.SC_OK) {
+			ret = response.getEntity(RangerRoles.class);
+		} else if (response.getStatus() == HttpServletResponse.SC_NOT_FOUND) {
+			ret = null;
+			LOG.error("Error getting Roles; service not found. secureMode=" + isSecureMode + ", user=" + user
+					+ ", response=" + response.getStatus() + ", serviceName=" + serviceName
+					+ ", " + "lastKnownRoleVersion=" + lastKnownRoleVersion
+					+ ", " + "lastActivationTimeInMillis=" + lastActivationTimeInMillis);
+			String exceptionMsg = response.hasEntity() ? response.getEntity(String.class) : null;
+
+			RangerServiceNotFoundException.throwExceptionIfServiceNotFound(serviceName, exceptionMsg);
+
+			LOG.warn("Received 404 error code with body:[" + exceptionMsg + "], Ignoring");
+		} else {
+			RESTResponse resp = RESTResponse.fromClientResponse(response);
+			LOG.warn("Error getting Roles. secureMode=" + isSecureMode + ", user=" + user + ", response=" + resp + ", serviceName=" + serviceName);
+			ret = null;
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerAdminRESTClient.getRolesIfUpdated(" + lastKnownRoleVersion + ", " + lastActivationTimeInMillis + "): ");
+		}
+
+		return ret;
+	}
+
+	@Override
 	public RangerRole createRole(final RangerRole request) throws Exception {
 		if(LOG.isDebugEnabled()) {
 			LOG.debug("==> RangerAdminRESTClient.createRole(" + request + ")");
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPluginInfo.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPluginInfo.java
index e3f9f15..4bd374e 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPluginInfo.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPluginInfo.java
@@ -43,6 +43,7 @@ public class RangerPluginInfo implements Serializable {
 
 	public static final int ENTITY_TYPE_POLICIES = 0;
 	public static final int ENTITY_TYPE_TAGS     = 1;
+	public static final int ENTITY_TYPE_ROLES	 = 2;
 
 	public static final String PLUGIN_INFO_POLICY_DOWNLOAD_TIME      = "policyDownloadTime";
 	public static final String PLUGIN_INFO_POLICY_DOWNLOADED_VERSION = "policyDownloadedVersion";
@@ -53,6 +54,10 @@ public class RangerPluginInfo implements Serializable {
 	public static final String PLUGIN_INFO_TAG_ACTIVATION_TIME       = "tagActivationTime";
 	public static final String PLUGIN_INFO_TAG_ACTIVE_VERSION        = "tagActiveVersion";
 
+	public static final String PLUGIN_INFO_ROLE_DOWNLOAD_TIME         = "roleDownloadTime";
+	public static final String PLUGIN_INFO_ROLE_DOWNLOADED_VERSION    = "roleDownloadedVersion";
+	public static final String PLUGIN_INFO_ROLE_ACTIVATION_TIME       = "roleActivationTime";
+	public static final String PLUGIN_INFO_ROLE_ACTIVE_VERSION        = "roleActiveVersion";
 
 	public static final String RANGER_ADMIN_LAST_POLICY_UPDATE_TIME  = "lastPolicyUpdateTime";
 	public static final String RANGER_ADMIN_LATEST_POLICY_VERSION    = "latestPolicyVersion";
@@ -271,6 +276,50 @@ public class RangerPluginInfo implements Serializable {
 		return StringUtils.isNotBlank(updateTimeString) ? Long.valueOf(updateTimeString) : null;
 	}
 
+	@JsonIgnore
+	public void setRoleDownloadTime(Long roleDownloadTime) {
+		getInfo().put(PLUGIN_INFO_ROLE_DOWNLOAD_TIME, roleDownloadTime == null ? null : Long.toString(roleDownloadTime));
+	}
+
+	@JsonIgnore
+	public Long getRoleDownloadTime() {
+		String downloadTimeString = getInfo().get(PLUGIN_INFO_ROLE_DOWNLOAD_TIME);
+		return StringUtils.isNotBlank(downloadTimeString) ? Long.valueOf(downloadTimeString) : null;
+	}
+
+	@JsonIgnore
+	public void setRoleDownloadedVersion(Long roleDownloadedVersion) {
+		getInfo().put(PLUGIN_INFO_ROLE_DOWNLOADED_VERSION, roleDownloadedVersion == null ? null : Long.toString(roleDownloadedVersion));
+	}
+
+	@JsonIgnore
+	public Long getRoleDownloadedVersion() {
+		String downloadedVersionString = getInfo().get(PLUGIN_INFO_ROLE_DOWNLOADED_VERSION);
+		return StringUtils.isNotBlank(downloadedVersionString) ? Long.valueOf(downloadedVersionString) : null;
+	}
+
+	@JsonIgnore
+	public void setRoleActivationTime(Long roleActivationTime) {
+		getInfo().put(PLUGIN_INFO_ROLE_ACTIVATION_TIME, roleActivationTime == null ? null : Long.toString(roleActivationTime));
+	}
+
+	@JsonIgnore
+	public Long getRoleActivationTime() {
+		String activationTimeString = getInfo().get(PLUGIN_INFO_ROLE_ACTIVATION_TIME);
+		return StringUtils.isNotBlank(activationTimeString) ? Long.valueOf(activationTimeString) : null;
+	}
+
+	@JsonIgnore
+	public void setRoleActiveVersion(Long roleActiveVersion) {
+		getInfo().put(PLUGIN_INFO_ROLE_ACTIVE_VERSION, roleActiveVersion == null ? null : Long.toString(roleActiveVersion));
+	}
+
+	@JsonIgnore
+	public Long getRoleActiveVersion() {
+		String activeVersionString = getInfo().get(PLUGIN_INFO_POLICY_ACTIVE_VERSION);
+		return StringUtils.isNotBlank(activeVersionString) ? Long.valueOf(activeVersionString) : null;
+	}
+
 	@Override
 	public String toString() {
 		StringBuilder sb = new StringBuilder();
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyDelta.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyDelta.java
index 1b69d8d..1d2b143 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyDelta.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyDelta.java
@@ -44,8 +44,9 @@ public class RangerPolicyDelta implements java.io.Serializable {
     public static final int CHANGE_TYPE_RANGER_ADMIN_START  = 5;
     public static final int CHANGE_TYPE_LOG_ERROR           = 6;
     public static final int CHANGE_TYPE_INVALIDATE_POLICY_DELTAS = 7;
+    public static final int CHANGE_TYPE_ROLE_UPDATE         = 8;
 
-    private static String[] changeTypeNames = { "POLICY_CREATE", "POLICY_UPDATE", "POLICY_DELETE", "SERVICE_CHANGE", "SERVICE_DEF_CHANGE", "RANGER_ADMIN_START", "LOG_ERROR", "INVALIDATE_POLICY_DELTAS" };
+    private static String[] changeTypeNames = { "POLICY_CREATE", "POLICY_UPDATE", "POLICY_DELETE", "SERVICE_CHANGE", "SERVICE_DEF_CHANGE", "RANGER_ADMIN_START", "LOG_ERROR", "INVALIDATE_POLICY_DELTAS", "ROLE_UPDATE" };
 
     private Long                id;
     private Integer             changeType;
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngine.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngine.java
index d201aa6..72628ea 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngine.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngine.java
@@ -29,6 +29,7 @@ import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
 import org.apache.ranger.plugin.util.GrantRevokeRequest;
 import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
+import org.apache.ranger.plugin.util.RangerRoles;
 import org.apache.ranger.plugin.util.ServicePolicies;
 
 public interface RangerPolicyEngine {
@@ -93,7 +94,11 @@ public interface RangerPolicyEngine {
 
 	List<RangerPolicy> getAllowedPolicies(String user, Set<String> userGroups, String accessType);
 
-	RangerPolicyEngine cloneWithDelta(ServicePolicies servicePolicies);
+	RangerPolicyEngine cloneWithDelta(ServicePolicies servicePolicies, RangerRoles rangerRoles);
+
+	RangerRoles getRangerRoles();
+
+	void setRangerRoles(RangerRoles rangerRoles);
 
 	Set<String> getRolesFromUserAndGroups(String user, Set<String> groups);
 
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineCache.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineCache.java
index 015ca09..5dae0c1 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineCache.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineCache.java
@@ -92,7 +92,7 @@ public class RangerPolicyEngineCache {
 
 
 		if (CollectionUtils.isNotEmpty(policies.getPolicyDeltas())) {
-			RangerPolicyEngine updatedEngine = policyEngine.cloneWithDelta(policies);
+			RangerPolicyEngine updatedEngine = policyEngine.cloneWithDelta(policies, policyEngine.getRangerRoles());
 			if (updatedEngine != null) {
 				policyEngineCache.put(policies.getServiceName(), updatedEngine);
 				ret = updatedEngine;
@@ -120,8 +120,6 @@ public class RangerPolicyEngineCache {
 			ret.setAuditMode(servicePolicies.getAuditMode());
 			ret.setPolicyVersion(servicePolicies.getPolicyVersion());
 			ret.setPolicyUpdateTime(servicePolicies.getPolicyUpdateTime());
-			ret.setUserRoles(servicePolicies.getUserRoles());
-			ret.setGroupRoles(servicePolicies.getGroupRoles());
 
 			Map<String, ServicePolicies.SecurityZoneInfo> securityZonesInfo = new HashMap<>();
 
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java
index c23a2d4..77648fd 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java
@@ -30,6 +30,7 @@ import org.apache.ranger.plugin.contextenricher.RangerContextEnricher;
 import org.apache.ranger.plugin.contextenricher.RangerTagForEval;
 import org.apache.ranger.plugin.model.RangerPolicy;
 import org.apache.ranger.plugin.model.RangerPolicyDelta;
+import org.apache.ranger.plugin.model.RangerRole;
 import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
 import org.apache.ranger.plugin.model.validation.RangerZoneResourceMatcher;
@@ -43,6 +44,8 @@ import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
 import org.apache.ranger.plugin.util.RangerPerfTracer;
 import org.apache.ranger.plugin.util.RangerResourceTrie;
 import org.apache.ranger.plugin.util.RangerPolicyDeltaUtil;
+import org.apache.ranger.plugin.util.RangerRoles;
+import org.apache.ranger.plugin.util.RangerRolesUtil;
 import org.apache.ranger.plugin.util.ServicePolicies;
 
 import java.util.ArrayList;
@@ -74,6 +77,7 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
 	private final RangerPolicyRepository tagPolicyRepository;
 
 	private List<RangerContextEnricher> allContextEnrichers;
+	private RangerRoles rangerRoles;
 
 	private boolean  useForwardedIPAddress;
 	private String[] trustedProxyAddresses;
@@ -81,18 +85,27 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
 	private Map<String, RangerPolicyRepository> policyRepositories = new HashMap<>();
 
 	private       Map<String, RangerResourceTrie>   trieMap;
+	private Map<String, Set<String>>                userRoleMapping;
+	private Map<String, Set<String>>                groupRoleMapping;
 	private       Map<String, String>               zoneTagServiceMap;
-	private final Map<String, Set<String>>          userRoleMapping;
-	private final Map<String, Set<String>>          groupRoleMapping;
 	private final RangerPluginContext               pluginContext;
 
 	public RangerPolicyEngineImpl(final RangerPolicyEngineImpl other, ServicePolicies servicePolicies) {
+		this(other,servicePolicies, null);
+	}
+
+	public RangerPolicyEngineImpl(final RangerPolicyEngineImpl other, ServicePolicies servicePolicies, RangerRoles rangerRoles) {
 
 		long                    policyVersion = servicePolicies.getPolicyVersion();
 
 		this.useForwardedIPAddress = other.useForwardedIPAddress;
 		this.trustedProxyAddresses = other.trustedProxyAddresses;
 
+		if (rangerRoles != null) {
+			this.rangerRoles = rangerRoles;
+			setUserGroupRoleMapping(rangerRoles);
+		}
+
 		this.pluginContext = other.pluginContext;
 
 		List<RangerPolicyDelta> defaultZoneDeltas = new ArrayList<>();
@@ -206,15 +219,19 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
 		}
 		this.allContextEnrichers = tmpList;
 
-                // Initialize role-related information
-                userRoleMapping = MapUtils.isNotEmpty(servicePolicies.getUserRoles()) ? servicePolicies.getUserRoles() : null;
-                groupRoleMapping = MapUtils.isNotEmpty(servicePolicies.getGroupRoles()) ? servicePolicies.getGroupRoles() : null;
-
 		reorderPolicyEvaluators();
 
 	}
 
+	public RangerPolicyEngineImpl(String appId, ServicePolicies servicePolicies, RangerPolicyEngineOptions options) {
+		this(appId, servicePolicies, options, null);
+	}
+
 	public RangerPolicyEngineImpl(String appId, ServicePolicies servicePolicies, RangerPolicyEngineOptions options, RangerPluginContext rangerPluginContext) {
+			this(appId, servicePolicies, options, rangerPluginContext, null);
+	}
+
+	public RangerPolicyEngineImpl(String appId, ServicePolicies servicePolicies, RangerPolicyEngineOptions options, RangerPluginContext rangerPluginContext, RangerRoles rangerRoles) {
 
 		if (LOG.isDebugEnabled()) {
 			LOG.debug("==> RangerPolicyEngineImpl(" + appId + ", " + servicePolicies + ", " + options + ", " + rangerPluginContext + ")");
@@ -235,6 +252,11 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
 
 		this.pluginContext = (rangerPluginContext != null) ? rangerPluginContext : new RangerPluginContext(servicePolicies.getServiceDef().getName());
 
+		if (rangerRoles != null) {
+			this.rangerRoles = rangerRoles;
+			setUserGroupRoleMapping(rangerRoles);
+		}
+
 		RangerAuthContext authContext = new RangerAuthContext(this, null, this.pluginContext);
 		this.pluginContext.setAuthContext(authContext);
 
@@ -305,10 +327,6 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
 			}
 		}
 
-                // Initialize role-related information
-                userRoleMapping = MapUtils.isNotEmpty(servicePolicies.getUserRoles()) ? servicePolicies.getUserRoles() : null;
-                groupRoleMapping = MapUtils.isNotEmpty(servicePolicies.getGroupRoles()) ? servicePolicies.getGroupRoles() : null;
-
 		RangerPerfTracer.log(perf);
 
 		if (PERF_POLICYENGINE_INIT_LOG.isDebugEnabled()) {
@@ -323,7 +341,7 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
 	}
 
 	@Override
-	public RangerPolicyEngine cloneWithDelta(ServicePolicies servicePolicies) {
+	public RangerPolicyEngine cloneWithDelta(ServicePolicies servicePolicies, RangerRoles rangerRoles) {
 		if (LOG.isDebugEnabled()) {
 			LOG.debug("==> cloneWithDelta(" + Arrays.toString(servicePolicies.getPolicyDeltas().toArray()) + ", " + servicePolicies.getPolicyVersion() + ")");
 		}
@@ -359,7 +377,7 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
 		}
 
 		if (isValidDeltas) {
-			ret = new RangerPolicyEngineImpl(this, servicePolicies);
+			ret = new RangerPolicyEngineImpl(this, servicePolicies, rangerRoles);
 		} else {
 			ret = null;
 		}
@@ -1328,7 +1346,13 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
         @Override
         public Set<String> getRolesFromUserAndGroups(String user, Set<String> groups) {
                 Set<String> allRoles = new HashSet<>();
-                if (MapUtils.isNotEmpty(userRoleMapping) && StringUtils.isNotEmpty(user)) {
+
+				if (rangerRoles != null ) {
+					userRoleMapping  = MapUtils.isNotEmpty(this.userRoleMapping)  ? this.userRoleMapping  : null;
+					groupRoleMapping = MapUtils.isNotEmpty(this.groupRoleMapping) ? this.groupRoleMapping : null;
+				}
+
+				if (MapUtils.isNotEmpty(userRoleMapping) && StringUtils.isNotEmpty(user)) {
                         Set<String> userRoles = userRoleMapping.get(user);
                         if (CollectionUtils.isNotEmpty(userRoles)) {
                                 allRoles.addAll(userRoles);
@@ -1353,6 +1377,14 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
                 return allRoles;
         }
 
+	public RangerRoles getRangerRoles() {
+		return this.rangerRoles;
+	}
+
+	public void setRangerRoles(RangerRoles rangerRoles) {
+		this.rangerRoles = rangerRoles;
+	}
+
 	public List<RangerPolicy> getResourcePolicies(String zoneName) {
 		RangerPolicyRepository zoneResourceRepository = policyRepositories.get(zoneName);
 		return zoneResourceRepository == null ? ListUtils.EMPTY_LIST : zoneResourceRepository.getPolicies();
@@ -1985,4 +2017,14 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
 		}
 		return other;
 	}
+
+	private void setUserGroupRoleMapping(RangerRoles rangerRoles) {
+		Set<RangerRole> rangerRoleSet = rangerRoles.getRangerRoles();
+		if (CollectionUtils.isNotEmpty(rangerRoleSet)) {
+			RangerRolesUtil rangerRolesUtil = new RangerRolesUtil();
+			rangerRolesUtil.init(rangerRoleSet);
+			userRoleMapping  = rangerRolesUtil.getUserRoleMapping();
+			groupRoleMapping = rangerRolesUtil.getGroupRoleMapping();
+		}
+	}
 }
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerAuthContext.java b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerAuthContext.java
index 842c58b..6cd1df6 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerAuthContext.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerAuthContext.java
@@ -39,6 +39,7 @@ import org.apache.ranger.plugin.policyengine.RangerResourceACLs;
 import org.apache.ranger.plugin.policyengine.RangerResourceAccessInfo;
 import org.apache.ranger.plugin.util.GrantRevokeRequest;
 import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
+import org.apache.ranger.plugin.util.RangerRoles;
 import org.apache.ranger.plugin.util.ServicePolicies;
 
 import java.util.Collection;
@@ -351,9 +352,9 @@ public class RangerAuthContext implements RangerPolicyEngine {
     }
 
     @Override
-    public RangerPolicyEngine cloneWithDelta(ServicePolicies servicePolicies) {
+    public RangerPolicyEngine cloneWithDelta(ServicePolicies servicePolicies, RangerRoles rangerRoles) {
         if (policyEngine != null) {
-            return policyEngine.cloneWithDelta(servicePolicies);
+            return policyEngine.cloneWithDelta(servicePolicies, rangerRoles);
         }
         return null;
     }
@@ -366,5 +367,14 @@ public class RangerAuthContext implements RangerPolicyEngine {
         return null;
     }
 
+    public RangerRoles getRangerRoles() {
+        return  policyEngine.getRangerRoles();
+    }
 
+    @Override
+    public void setRangerRoles(RangerRoles rangerRoles) {
+        if (policyEngine != null) {
+            policyEngine.setRangerRoles(rangerRoles);
+        }
+    }
 }
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java
index cf833b7..1325a40 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java
@@ -77,6 +77,8 @@ public class RangerBasePlugin {
 	private Timer                     policyEngineRefreshTimer;
 	private RangerAuthContextListener authContextListener;
 	private AuditProviderFactory      auditProviderFactory;
+	private RangerRolesProvider		  rangerRolesProvider;
+	private RangerRoles               rangerRoles;
 
 	private final BlockingQueue<DownloadTrigger> policyDownloadQueue = new LinkedBlockingQueue<>();
 	private final DownloadTrigger                accessTrigger       = new DownloadTrigger();
@@ -151,6 +153,14 @@ public class RangerBasePlugin {
 		this.clusterName = clusterName;
 	}
 
+	public RangerRoles getRangerRoles() {
+		return this.rangerRoles;
+	}
+
+	public void setRangerRoles(RangerRoles rangerRoles) {
+		this.rangerRoles = rangerRoles;
+	}
+
 	public RangerServiceDef getServiceDef() {
 		RangerPolicyEngine policyEngine = this.policyEngine;
 
@@ -225,7 +235,9 @@ public class RangerBasePlugin {
 
 		RangerAdminClient admin = createAdminClient(serviceName, appId, propertyPrefix);
 
-		refresher = new PolicyRefresher(this, serviceType, appId, serviceName, admin, policyDownloadQueue, cacheDir);
+		rangerRolesProvider = new RangerRolesProvider(serviceType, appId, serviceName, admin,  cacheDir);
+
+		refresher = new PolicyRefresher(this, serviceType, appId, serviceName, admin, policyDownloadQueue, cacheDir, rangerRolesProvider);
 		refresher.setDaemon(true);
 		refresher.startRefresher();
 
@@ -279,6 +291,7 @@ public class RangerBasePlugin {
 			ServicePolicies    servicePolicies = null;
 			boolean            isValid         = true;
 			boolean            usePolicyDeltas = false;
+			boolean            updateRangerRolesOnly = false;
 
 			if (policies == null) {
 				policies = getDefaultSvcPolicies();
@@ -287,7 +300,7 @@ public class RangerBasePlugin {
 					isValid = false;
 				}
 			} else {
-				if ((policies.getPolicies() == null && policies.getPolicyDeltas() == null) || (policies.getPolicies() != null && policies.getPolicyDeltas() != null)) {
+				if ((policies.getPolicies() != null && policies.getPolicyDeltas() != null)) {
 					LOG.error("Invalid servicePolicies: Both policies and policy-deltas cannot be null OR both of them cannot be non-null");
 					isValid = false;
 				} else if (policies.getPolicies() != null) {
@@ -302,6 +315,9 @@ public class RangerBasePlugin {
 						isValid = false;
 						LOG.error("Could not apply deltas=" + Arrays.toString(policies.getPolicyDeltas().toArray()));
 					}
+				} else if (policies.getPolicies() == null && policies.getPolicyDeltas() == null && rangerRoles != null) {
+					// When no policies changes and only the role changes happens then update the policyengine with Role changes only.
+					updateRangerRolesOnly = true;
 				} else {
 					LOG.error("Should not get here!!");
 					isValid = false;
@@ -311,11 +327,13 @@ public class RangerBasePlugin {
 			if (isValid) {
 				RangerPolicyEngine newPolicyEngine = null;
 
-				if (!usePolicyDeltas) {
+				if(updateRangerRolesOnly) {
+					this.policyEngine.setRangerRoles(rangerRoles);
+				} else if (!usePolicyDeltas) {
 					if (LOG.isDebugEnabled()) {
 						LOG.debug("policies are not null. Creating engine from policies");
 					}
-					newPolicyEngine = new RangerPolicyEngineImpl(appId, policies, policyEngineOptions, rangerPluginContext);
+					newPolicyEngine = new RangerPolicyEngineImpl(appId, policies, policyEngineOptions, rangerPluginContext, rangerRoles);
 				} else {
 					if (LOG.isDebugEnabled()) {
 						LOG.debug("policy-deltas are not null");
@@ -324,7 +342,7 @@ public class RangerBasePlugin {
 						if (LOG.isDebugEnabled()) {
 							LOG.debug("Non empty policy-deltas found. Cloning engine using policy-deltas");
 						}
-						newPolicyEngine = oldPolicyEngine.cloneWithDelta(policies);
+						newPolicyEngine = oldPolicyEngine.cloneWithDelta(policies, rangerRoles);
 						if (newPolicyEngine != null) {
 							if (LOG.isDebugEnabled()) {
 								LOG.debug("Applied policyDeltas=" + Arrays.toString(policies.getPolicyDeltas().toArray()) + ")");
@@ -334,7 +352,7 @@ public class RangerBasePlugin {
 								LOG.debug("Failed to apply policyDeltas=" + Arrays.toString(policies.getPolicyDeltas().toArray()) + "), Creating engine from policies");
 								LOG.debug("Creating new engine from servicePolicies:[" + servicePolicies + "]");
 							}
-							newPolicyEngine = new RangerPolicyEngineImpl(appId, servicePolicies, policyEngineOptions, rangerPluginContext);
+							newPolicyEngine = new RangerPolicyEngineImpl(appId, servicePolicies, policyEngineOptions, rangerPluginContext, rangerRoles);
 						}
 					} else {
 						if (LOG.isDebugEnabled()) {
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/PolicyRefresher.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/PolicyRefresher.java
index 0e52c31..d4d7902 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/util/PolicyRefresher.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/PolicyRefresher.java
@@ -47,6 +47,7 @@ public class PolicyRefresher extends Thread {
 	private final String            serviceName;
 	private final RangerAdminClient rangerAdmin;
 	private final BlockingQueue<DownloadTrigger> policyDownloadQueue;
+	private final RangerRolesProvider rangerRolesProvider;
 
 	private final String            cacheFileName;
 	private final String            cacheDir;
@@ -58,7 +59,7 @@ public class PolicyRefresher extends Thread {
 	private boolean policiesSetInPlugin;
 	private boolean serviceDefSetInPlugin;
 
-	public PolicyRefresher(RangerBasePlugin plugIn, String serviceType, String appId, String serviceName, RangerAdminClient rangerAdmin, BlockingQueue<DownloadTrigger> policyDownloadQueue, String cacheDir) {
+	public PolicyRefresher(RangerBasePlugin plugIn, String serviceType, String appId, String serviceName, RangerAdminClient rangerAdmin, BlockingQueue<DownloadTrigger> policyDownloadQueue, String cacheDir, RangerRolesProvider rangerRolesProvider) {
 		if(LOG.isDebugEnabled()) {
 			LOG.debug("==> PolicyRefresher(serviceName=" + serviceName + ").PolicyRefresher()");
 		}
@@ -68,6 +69,7 @@ public class PolicyRefresher extends Thread {
 		this.serviceName       = serviceName;
 		this.rangerAdmin       = rangerAdmin;
 		this.policyDownloadQueue = policyDownloadQueue;
+		this.rangerRolesProvider = rangerRolesProvider;
 
 		if(StringUtils.isEmpty(appId)) {
 			appId = serviceType;
@@ -133,7 +135,7 @@ public class PolicyRefresher extends Thread {
 	}
 
 	public void startRefresher() {
-
+		loadRoles();
 		loadPolicy();
 
 		super.start();
@@ -158,6 +160,7 @@ public class PolicyRefresher extends Thread {
 		while(true) {
 			try {
 				DownloadTrigger trigger = policyDownloadQueue.take();
+				loadRoles();
 				loadPolicy();
 				trigger.signalCompletion();
 			} catch(InterruptedException excp) {
@@ -428,4 +431,17 @@ public class PolicyRefresher extends Thread {
 			LOG.debug("<== PolicyRefresher.disableCache(serviceName=" + serviceName + ")");
 		}
 	}
+
+	private void loadRoles() {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> PolicyRefresher(serviceName=" + serviceName + ").loadRoles()");
+		}
+
+		//Load the Ranger UserGroup Roles
+		rangerRolesProvider.loadUserGroupRoles(plugIn);
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== PolicyRefresher(serviceName=" + serviceName + ").loadRoles()");
+		}
+	}
 }
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTUtils.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTUtils.java
index bdb77e7..d612e7f 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTUtils.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTUtils.java
@@ -45,6 +45,9 @@ public class RangerRESTUtils {
 	public static final String REST_URL_SECURE_SERVICE_GRANT_ACCESS              = "/service/plugins/secure/services/grant/";
 	public static final String REST_URL_SECURE_SERVICE_REVOKE_ACCESS             = "/service/plugins/secure/services/revoke/";
 
+	public static final String REST_URL_ROLE_GET_FOR_SECURE_SERVICE_IF_UPDATED   = "/service/roles/secure/download/";
+	public static final String REST_URL_ROLE_GET_FOR_SERVICE_IF_UPDATED          = "/service/roles/download/";
+
 	public static final String REST_URL_SERVICE_CREATE_ROLE              = "/service/public/v2/api/roles/";
 	public static final String REST_URL_SERVICE_DROP_ROLE         = "/service/public/v2/api/roles/name/";
 	public static final String REST_URL_SERVICE_GET_ALL_ROLES         = "/service/public/v2/api/roles/names/";
@@ -53,6 +56,9 @@ public class RangerRESTUtils {
 	public static final String REST_URL_SERVICE_GRANT_ROLE              = "/service/public/v2/api/roles/grant/";
 	public static final String REST_URL_SERVICE_REVOKE_ROLE              = "/service/public/v2/api/roles/revoke/";
 
+	public static final String REST_URL_SERVICE_SERCURE_GET_USER_GROUP_ROLES = "/service/roles/secure/download/";
+	public static final String REST_URL_SERVICE_GET_USER_GROUP_ROLES         = "/service/roles/download/";
+
 	public static final String REST_URL_GET_SERVICE_TAGS_IF_UPDATED = "/service/tags/download/";
 	public static final String REST_URL_GET_SECURE_SERVICE_TAGS_IF_UPDATED = "/service/tags/secure/download/";
 	public static final String SERVICE_NAME_PARAM = "serviceName";
@@ -68,6 +74,8 @@ public class RangerRESTUtils {
 	public static final String REST_PARAM_LAST_ACTIVATION_TIME = "lastActivationTime";
 	public static final String REST_PARAM_PLUGIN_ID                 = "pluginId";
 
+	public static final String REST_PARAM_LAST_KNOWN_ROLE_VERSION = "lastKnownRoleVersion";
+
 	private static final int MAX_PLUGIN_ID_LEN = 255;
 	
 	public static final String REST_PARAM_CLUSTER_NAME   = "clusterName";
@@ -115,11 +123,23 @@ public class RangerRESTUtils {
 		return url;
 	}
 
+	public String getUrlForRoleUpdate(String baseUrl, String serviceName) {
+		String url = baseUrl + REST_URL_ROLE_GET_FOR_SERVICE_IF_UPDATED + serviceName;
+
+		return url;
+	}
+
+
 	public String getSecureUrlForPolicyUpdate(String baseUrl, String serviceName) {
 		String url = baseUrl + REST_URL_POLICY_GET_FOR_SECURE_SERVICE_IF_UPDATED + serviceName;
 		return url;
 	}
 
+	public String getSecureUrlForRoleUpdate(String baseUrl, String serviceName) {
+		String url = baseUrl + REST_URL_ROLE_GET_FOR_SECURE_SERVICE_IF_UPDATED + serviceName;
+		return url;
+	}
+
 	public String getUrlForTagUpdate(String baseUrl, String serviceName) {
 		String url = baseUrl + REST_URL_GET_SERVICE_TAGS_IF_UPDATED + serviceName;
 
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRoles.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRoles.java
new file mode 100644
index 0000000..354bf44
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRoles.java
@@ -0,0 +1,78 @@
+/*
+ * 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.ranger.plugin.util;
+
+import org.apache.ranger.plugin.model.RangerRole;
+import org.codehaus.jackson.annotate.JsonAutoDetect;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Set;
+
+@JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown=true)
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class RangerRoles implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String           serviceName;
+    private Long             roleVersion;
+    private Date             roleUpdateTime;
+    private Set<RangerRole>  rangerRoles;
+
+    public String getServiceName() {
+        return serviceName;
+    }
+
+    public void setServiceName(String serviceName) {
+        this.serviceName = serviceName;
+    }
+
+    public Long getRoleVersion() {
+        return roleVersion;
+    }
+
+    public void setRoleVersion(Long roleVersion) {
+        this.roleVersion = roleVersion;
+    }
+
+    public Date getRoleUpdateTime() {
+        return roleUpdateTime;
+    }
+
+    public void setRoleUpdateTime(Date roleUpdateTime) {
+        this.roleUpdateTime = roleUpdateTime;
+    }
+
+    public Set<RangerRole> getRangerRoles(){
+        return this.rangerRoles;
+    }
+
+    public void setRangerRoles(Set<RangerRole> rangerRoles){
+        this.rangerRoles = rangerRoles;
+    }
+}
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRolesProvider.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRolesProvider.java
new file mode 100644
index 0000000..5ba3cca
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRolesProvider.java
@@ -0,0 +1,352 @@
+/*
+ * 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.ranger.plugin.util;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.admin.client.RangerAdminClient;
+import org.apache.ranger.authorization.hadoop.config.RangerConfiguration;
+import org.apache.ranger.plugin.service.RangerBasePlugin;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.Reader;
+import java.io.Writer;
+
+
+public class RangerRolesProvider {
+	private static final Log LOG = LogFactory.getLog(RangerRolesProvider.class);
+
+	private static final Log PERF_POLICYENGINE_INIT_LOG = RangerPerfTracer.getPerfLogger("policyengine.init");
+
+	private final String            serviceType;
+	private final String            serviceName;
+	private final RangerAdminClient rangerAdmin;
+
+	private final String            cacheFileName;
+	private final String			cacheFileNamePrefix;
+	private final String            cacheDir;
+	private final Gson              gson;
+	private final boolean           disableCacheIfServiceNotFound;
+
+	private long	lastActivationTimeInMillis;
+	private long    lastKnownRoleVersion = -1L;
+	private boolean rangerUserGroupRolesSetInPlugin;
+	private boolean serviceDefSetInPlugin;
+
+	public RangerRolesProvider(String serviceType, String appId, String serviceName, RangerAdminClient rangerAdmin, String cacheDir) {
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerRolesProvider(serviceName=" + serviceName + ").RangerRolesProvider()");
+		}
+
+		this.serviceType = serviceType;
+		this.serviceName = serviceName;
+		this.rangerAdmin = rangerAdmin;
+
+
+		if (StringUtils.isEmpty(appId)) {
+			appId = serviceType;
+		}
+
+		cacheFileNamePrefix = "roles";
+		String cacheFilename = String.format("%s_%s_%s.json", appId, serviceName, cacheFileNamePrefix);
+		cacheFilename = cacheFilename.replace(File.separatorChar, '_');
+		cacheFilename = cacheFilename.replace(File.pathSeparatorChar, '_');
+
+		this.cacheFileName = cacheFilename;
+		this.cacheDir = cacheDir;
+
+		Gson gson = null;
+		try {
+			gson = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z").create();
+		} catch (Throwable excp) {
+			LOG.fatal("RangerRolesProvider(): failed to create GsonBuilder object", excp);
+		}
+		this.gson = gson;
+
+		String propertyPrefix = "ranger.plugin." + serviceType;
+		disableCacheIfServiceNotFound = RangerConfiguration.getInstance().getBoolean(propertyPrefix + ".disable.cache.if.servicenotfound", true);
+
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerRolesProvider(serviceName=" + serviceName + ").RangerRolesProvider()");
+		}
+	}
+
+	public long getLastActivationTimeInMillis() {
+		return lastActivationTimeInMillis;
+	}
+
+	public void setLastActivationTimeInMillis(long lastActivationTimeInMillis) {
+		this.lastActivationTimeInMillis = lastActivationTimeInMillis;
+	}
+
+	public void loadUserGroupRoles(RangerBasePlugin plugIn) {
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerRolesProvider(serviceName= " + serviceName  + " serviceType= " + serviceType +").loadUserGroupRoles()");
+		}
+
+		RangerPerfTracer perf = null;
+
+		if(RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYENGINE_INIT_LOG)) {
+			perf = RangerPerfTracer.getPerfTracer(PERF_POLICYENGINE_INIT_LOG, "RangerRolesProvider.loadUserGroupRoles(serviceName=" + serviceName + ")");
+			long freeMemory = Runtime.getRuntime().freeMemory();
+			long totalMemory = Runtime.getRuntime().totalMemory();
+			PERF_POLICYENGINE_INIT_LOG.debug("In-Use memory: " + (totalMemory-freeMemory) + ", Free memory:" + freeMemory);
+		}
+
+		try {
+			//load userGroupRoles from ranger admin
+			RangerRoles rangerRoles = loadUserGroupRolesFromAdmin();
+
+			if (rangerRoles == null) {
+				//if userGroupRoles fetch from ranger Admin Fails, load from cache
+				if (!rangerUserGroupRolesSetInPlugin) {
+					rangerRoles = loadUserGroupRolesFromCache();
+				}
+			}
+
+			if (PERF_POLICYENGINE_INIT_LOG.isDebugEnabled()) {
+				long freeMemory = Runtime.getRuntime().freeMemory();
+				long totalMemory = Runtime.getRuntime().totalMemory();
+				PERF_POLICYENGINE_INIT_LOG.debug("In-Use memory: " + (totalMemory - freeMemory) + ", Free memory:" + freeMemory);
+			}
+
+			if (rangerRoles != null) {
+				plugIn.setRangerRoles(rangerRoles);
+				rangerUserGroupRolesSetInPlugin = true;
+				setLastActivationTimeInMillis(System.currentTimeMillis());
+				lastKnownRoleVersion = rangerRoles.getRoleVersion();
+			} else {
+				if (!rangerUserGroupRolesSetInPlugin && !serviceDefSetInPlugin) {
+					plugIn.setRangerRoles(null);
+					serviceDefSetInPlugin = true;
+				}
+			}
+		} catch (RangerServiceNotFoundException snfe) {
+			if (disableCacheIfServiceNotFound) {
+				disableCache();
+				plugIn.setRangerRoles(null);
+				setLastActivationTimeInMillis(System.currentTimeMillis());
+				lastKnownRoleVersion = -1L;
+				serviceDefSetInPlugin = true;
+			}
+		} catch (Exception excp) {
+			LOG.error("Encountered unexpected exception, ignoring..", excp);
+		}
+
+		RangerPerfTracer.log(perf);
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerRolesProvider(serviceName=" + serviceName + ").loadUserGroupRoles()");
+		}
+	}
+
+	private RangerRoles loadUserGroupRolesFromAdmin() throws RangerServiceNotFoundException {
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerRolesProvider(serviceName=" + serviceName + ").loadUserGroupRolesFromAdmin()");
+		}
+
+		RangerRoles rangerRoles = null;
+
+		RangerPerfTracer perf = null;
+
+		if(RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYENGINE_INIT_LOG)) {
+			perf = RangerPerfTracer.getPerfTracer(PERF_POLICYENGINE_INIT_LOG, "RangerRolesProvider.loadUserGroupRolesFromAdmin(serviceName=" + serviceName + ")");
+		}
+
+		try {
+			rangerRoles = rangerAdmin.getRolesIfUpdated(lastKnownRoleVersion, lastActivationTimeInMillis);
+
+			boolean isUpdated = rangerRoles != null;
+
+			if(isUpdated) {
+				long newVersion = rangerRoles.getRoleVersion() == null ? -1 : rangerRoles.getRoleVersion().longValue();
+				saveToCache(rangerRoles);
+				LOG.info("RangerRolesProvider(serviceName=" + serviceName + "): found updated version. lastKnownRoleVersion=" + lastKnownRoleVersion + "; newVersion=" + newVersion );
+			} else {
+				if(LOG.isDebugEnabled()) {
+					LOG.debug("RangerRolesProvider(serviceName=" + serviceName + ").run(): no update found. lastKnownRoleVersion=" + lastKnownRoleVersion );
+				}
+			}
+		} catch (RangerServiceNotFoundException snfe) {
+			LOG.error("RangerRolesProvider(serviceName=" + serviceName + "): failed to find service. Will clean up local cache of rangerRoles (" + lastKnownRoleVersion + ")", snfe);
+			throw snfe;
+		} catch (Exception excp) {
+			LOG.error("RangerRolesProvider(serviceName=" + serviceName + "): failed to refresh rangerRoles. Will continue to use last known version of rangerRoles (" + "lastKnowRoleVersion= " + lastKnownRoleVersion, excp);
+			rangerRoles = null;
+		}
+
+		RangerPerfTracer.log(perf);
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerRolesProvider(serviceName=" + serviceName + " serviceType= " + serviceType + " ).loadUserGroupRolesFromAdmin()");
+		 }
+
+		 return rangerRoles;
+	}
+
+	private RangerRoles loadUserGroupRolesFromCache() {
+
+		RangerRoles rangerRoles = null;
+
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerRolesProvider(serviceName=" + serviceName + ").loadUserGroupRolesFromCache()");
+		}
+
+		File cacheFile = cacheDir == null ? null : new File(cacheDir + File.separator + cacheFileName);
+
+		if (cacheFile != null && cacheFile.isFile() && cacheFile.canRead()) {
+			Reader reader = null;
+
+			RangerPerfTracer perf = null;
+
+			if (RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYENGINE_INIT_LOG)) {
+				perf = RangerPerfTracer.getPerfTracer(PERF_POLICYENGINE_INIT_LOG, "RangerRolesProvider.loadUserGroupRolesFromCache(serviceName=" + serviceName + ")");
+			}
+
+			try {
+				reader = new FileReader(cacheFile);
+
+				rangerRoles = gson.fromJson(reader, RangerRoles.class);
+
+				if (rangerRoles != null) {
+					if (!StringUtils.equals(serviceName, rangerRoles.getServiceName())) {
+						LOG.warn("ignoring unexpected serviceName '" + rangerRoles.getServiceName() + "' in cache file '" + cacheFile.getAbsolutePath() + "'");
+
+						rangerRoles.setServiceName(serviceName);
+					}
+
+					lastKnownRoleVersion = rangerRoles.getRoleVersion() == null ? -1 : rangerRoles.getRoleVersion().longValue();
+				}
+			} catch (Exception excp) {
+				LOG.error("failed to load userGroupRoles from cache file " + cacheFile.getAbsolutePath(), excp);
+			} finally {
+				RangerPerfTracer.log(perf);
+
+				if (reader != null) {
+					try {
+						reader.close();
+					} catch (Exception excp) {
+						LOG.error("error while closing opened cache file " + cacheFile.getAbsolutePath(), excp);
+					}
+				}
+			}
+		} else {
+			LOG.warn("cache file does not exist or not readable '" + (cacheFile == null ? null : cacheFile.getAbsolutePath()) + "'");
+		}
+
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerRolesProvider(serviceName=" + serviceName + ").RangerRolesProvider()");
+		}
+
+		return rangerRoles;
+	}
+
+	public void saveToCache(RangerRoles rangerRoles) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerRolesProvider(serviceName=" + serviceName + ").saveToCache()");
+		}
+
+		if(rangerRoles != null) {
+			File cacheFile = null;
+			if (cacheDir != null) {
+				// Create the cacheDir if it doesn't already exist
+				File cacheDirTmp = new File(cacheDir);
+				if (cacheDirTmp.exists()) {
+					cacheFile =  new File(cacheDir + File.separator + cacheFileName);
+				} else {
+					try {
+						cacheDirTmp.mkdirs();
+						cacheFile =  new File(cacheDir + File.separator + cacheFileName);
+					} catch (SecurityException ex) {
+						LOG.error("Cannot create cache directory", ex);
+					}
+				}
+			}
+
+			if(cacheFile != null) {
+
+				RangerPerfTracer perf = null;
+
+				if(RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYENGINE_INIT_LOG)) {
+					perf = RangerPerfTracer.getPerfTracer(PERF_POLICYENGINE_INIT_LOG, "RangerRolesProvider.saveToCache(serviceName=" + serviceName + ")");
+				}
+
+				Writer writer = null;
+
+				try {
+					writer = new FileWriter(cacheFile);
+
+			        gson.toJson(rangerRoles, writer);
+		        } catch (Exception excp) {
+					LOG.error("failed to save rangerRoles to cache file '" + cacheFile.getAbsolutePath() + "'", excp);
+		        } finally {
+					if(writer != null) {
+						try {
+							writer.close();
+						} catch(Exception excp) {
+							LOG.error("error while closing opened cache file '" + cacheFile.getAbsolutePath() + "'", excp);
+						}
+					}
+				}
+
+				RangerPerfTracer.log(perf);
+			}
+		} else {
+			LOG.info("rangerRoles is null. Nothing to save in cache");
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerRolesProvider.saveToCache(serviceName=" + serviceName + ")");
+		}
+	}
+
+	private void disableCache() {
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerRolesProvider.disableCache(serviceName=" + serviceName + ")");
+		}
+
+		File cacheFile = cacheDir == null ? null : new File(cacheDir + File.separator + cacheFileName);
+
+		if(cacheFile != null && cacheFile.isFile() && cacheFile.canRead()) {
+			LOG.warn("Cleaning up local RangerRoles cache");
+			String renamedCacheFile = cacheFile.getAbsolutePath() + "_" + System.currentTimeMillis();
+			if (!cacheFile.renameTo(new File(renamedCacheFile))) {
+				LOG.error("Failed to move " + cacheFile.getAbsolutePath() + " to " + renamedCacheFile);
+			} else {
+				LOG.warn("Moved " + cacheFile.getAbsolutePath() + " to " + renamedCacheFile);
+			}
+		} else {
+			if (LOG.isDebugEnabled()) {
+				LOG.debug("No local RangerRoles cache found. No need to disable it!");
+			}
+		}
+
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerRolesProvider.disableCache(serviceName=" + serviceName + ")");
+		}
+	}
+}
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRolesUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRolesUtil.java
new file mode 100644
index 0000000..c96d250
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRolesUtil.java
@@ -0,0 +1,106 @@
+/*
+ * 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.ranger.plugin.util;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.ranger.plugin.model.RangerRole;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class RangerRolesUtil {
+
+    Map<String, Set<String>> userRoleMapping = new HashMap<>();
+    Map<String, Set<String>> groupRoleMapping = new HashMap<>();
+
+    public Map<String, Set<String>> getUserRoleMapping() {
+        return this.userRoleMapping;
+    }
+
+    public Map<String, Set<String>> getGroupRoleMapping() {
+        return this.groupRoleMapping;
+    }
+
+    public RangerRoles init(Set<RangerRole> rangerRoles) {
+        RangerRoles ret = new RangerRoles();
+        if (rangerRoles != null) {
+            if (CollectionUtils.isNotEmpty(rangerRoles)) {
+                for (RangerRole role : rangerRoles) {
+                    Set<RangerRole> containedRoles = getAllContainedRoles(rangerRoles, role);
+                    buildMap(userRoleMapping, role, containedRoles, true);
+                    buildMap(groupRoleMapping, role, containedRoles, false);
+                }
+            }
+        }
+        return ret;
+    }
+
+    public Set<RangerRole> getAllContainedRoles(Set<RangerRole> rangerRoles, RangerRole role) {
+        Set<RangerRole> allRoles = new HashSet<>();
+        allRoles.add(role);
+        addContainedRoles(allRoles, rangerRoles, role);
+        return allRoles;
+    }
+
+    private void addContainedRoles(Set<RangerRole> allRoles, Set<RangerRole> rangerRoles, RangerRole role) {
+        List<RangerRole.RoleMember> roleMembers = role.getRoles();
+        for (RangerRole.RoleMember roleMember : roleMembers) {
+            RangerRole containedRole = getContainedRole(rangerRoles, roleMember.getName());
+            if (containedRole!= null && !allRoles.contains(containedRole)) {
+                allRoles.add(containedRole);
+                addContainedRoles(allRoles, rangerRoles, containedRole);
+            }
+        }
+    }
+
+    public void buildMap(Map<String, Set<String>> map, RangerRole role, Set<RangerRole> containedRoles, boolean isUser) {
+        buildMap(map, role, role.getName(), isUser);
+        for (RangerRole containedRole : containedRoles) {
+            buildMap(map, containedRole, role.getName(), isUser);
+        }
+    }
+
+    public void buildMap(Map<String, Set<String>> map, RangerRole role, String roleName, boolean isUser) {
+        for (RangerRole.RoleMember userOrGroup : isUser ? role.getUsers() : role.getGroups()) {
+            if (StringUtils.isNotEmpty(userOrGroup.getName())) {
+                Set<String> roleNames = map.get(userOrGroup.getName());
+                if (roleNames == null) {
+                    roleNames = new HashSet<>();
+                    map.put(userOrGroup.getName(), roleNames);
+                }
+                roleNames.add(roleName);
+            }
+        }
+    }
+
+    public RangerRole getContainedRole(Set<RangerRole> rangerRoles, String role) {
+        return (rangerRoles
+                .stream()
+                .filter(containedRole -> role.equals(containedRole.getName()))
+                .findAny()
+                .orElse(null));
+    }
+}
+
+
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/ServicePolicies.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/ServicePolicies.java
index 8c63434..f6beac6 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/util/ServicePolicies.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/ServicePolicies.java
@@ -25,7 +25,6 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -60,8 +59,6 @@ public class ServicePolicies implements java.io.Serializable {
 	private TagPolicies        tagPolicies;
 	private Map<String, SecurityZoneInfo> securityZones;
 	private List<RangerPolicyDelta> policyDeltas;
-	private Map<String, Set<String>> userRoles;
-	private Map<String, Set<String>> groupRoles;
 
 	/**
 	 * @return the serviceName
@@ -162,12 +159,6 @@ public class ServicePolicies implements java.io.Serializable {
 		this.securityZones = securityZones;
 	}
 
-	public Map<String, Set<String>> getUserRoles() { return userRoles; }
-	public Map<String, Set<String>> getGroupRoles() { return groupRoles; }
-
-	public void setUserRoles(Map<String, Set<String>> userRoles) { this.userRoles = userRoles; }
-	public void setGroupRoles(Map<String, Set<String>> groupRoles) { this.groupRoles = groupRoles; }
-
 	@Override
 	public String toString() {
 		return "serviceName=" + serviceName + ", "
@@ -179,9 +170,7 @@ public class ServicePolicies implements java.io.Serializable {
 			 	+ "policyDeltas=" + policyDeltas + ", "
 			 	+ "serviceDef=" + serviceDef + ", "
 			 	+ "auditMode=" + auditMode + ", "
-			 	+ "securityZones=" + securityZones + ", "
-				+ "userRoles=" + userRoles + ", "
-				+ "groupRoles=" + groupRoles + ", "
+				+ "securityZones=" + securityZones
 				;
 	}
 	public List<RangerPolicyDelta> getPolicyDeltas() { return this.policyDeltas; }
@@ -363,8 +352,6 @@ public class ServicePolicies implements java.io.Serializable {
 		ret.setServiceDef(source.getServiceDef());
 		ret.setPolicyUpdateTime(source.getPolicyUpdateTime());
 		ret.setSecurityZones(source.getSecurityZones());
-		ret.setUserRoles(source.getUserRoles());
-		ret.setGroupRoles(source.getGroupRoles());
 		ret.setPolicies(Collections.emptyList());
 		ret.setPolicyDeltas(null);
 		if (source.getTagPolicies() != null) {
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java
index d0e0cfc..83bbffc 100644
--- a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java
@@ -36,6 +36,7 @@ import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler;
 import org.apache.ranger.plugin.contextenricher.RangerTagForEval;
 import org.apache.ranger.plugin.model.RangerPolicy;
 import org.apache.ranger.plugin.model.RangerPolicyDelta;
+import org.apache.ranger.plugin.model.RangerRole;
 import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.model.RangerValiditySchedule;
 import org.apache.ranger.plugin.model.validation.RangerValidityScheduleValidator;
@@ -44,6 +45,7 @@ import org.apache.ranger.plugin.policyengine.TestPolicyEngine.PolicyEngineTestCa
 import org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator;
 import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
 import org.apache.ranger.plugin.util.RangerRequestedResources;
+import org.apache.ranger.plugin.util.RangerRoles;
 import org.apache.ranger.plugin.util.ServicePolicies;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
@@ -57,7 +59,9 @@ import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
@@ -410,8 +414,6 @@ public class TestPolicyEngine {
 		servicePolicies.setServiceDef(testCase.serviceDef);
 		servicePolicies.setPolicies(testCase.policies);
 		servicePolicies.setSecurityZones(testCase.securityZones);
-		servicePolicies.setUserRoles(testCase.userRoles);
-		servicePolicies.setGroupRoles(testCase.groupRoles);
 
 		if (StringUtils.isNotBlank(testCase.auditMode)) {
 			servicePolicies.setAuditMode(testCase.auditMode);
@@ -446,7 +448,43 @@ public class TestPolicyEngine {
 		RangerPluginContext pluginContext = new RangerPluginContext("hive");
 		pluginContext.setClusterName("cl1");
 		pluginContext.setClusterType("on-prem");
-		RangerPolicyEngine policyEngine = new RangerPolicyEngineImpl(testName, servicePolicies, policyEngineOptions,  pluginContext);
+
+		RangerRoles rangerRoles = new RangerRoles();
+		rangerRoles.setServiceName(testCase.serviceName);
+		rangerRoles.setRoleVersion(-1L);
+		Set<RangerRole> rangerRoleSet = new HashSet<>();
+
+		Map<String, Set<String>> userRoleMapping = testCase.userRoles;
+		Map<String, Set<String>> groupRoleMapping = testCase.groupRoles;
+		if (userRoleMapping != null) {
+			for (Map.Entry<String, Set<String>> userRole : userRoleMapping.entrySet()) {
+				String user = userRole.getKey();
+				Set<String> userRoles = userRole.getValue();
+				RangerRole.RoleMember userRoleMember = new RangerRole.RoleMember(user, true);
+				List<RangerRole.RoleMember> userRoleMembers = Arrays.asList(userRoleMember);
+				for (String usrRole : userRoles) {
+					RangerRole rangerUserRole = new RangerRole(usrRole, usrRole, null, userRoleMembers, null);
+					rangerRoleSet.add(rangerUserRole);
+				}
+			}
+		}
+
+		if (groupRoleMapping != null) {
+			for (Map.Entry<String, Set<String>> groupRole : groupRoleMapping.entrySet()) {
+				String group = groupRole.getKey();
+				Set<String> groupRoles = groupRole.getValue();
+				RangerRole.RoleMember groupRoleMember = new RangerRole.RoleMember(group, true);
+				List<RangerRole.RoleMember> groupRoleMembers = Arrays.asList(groupRoleMember);
+				for (String grpRole : groupRoles) {
+					RangerRole rangerGroupRole = new RangerRole(grpRole, grpRole, null, groupRoleMembers, null);
+					rangerRoleSet.add(rangerGroupRole);
+				}
+			}
+		}
+
+		rangerRoles.setRangerRoles(rangerRoleSet);
+
+		RangerPolicyEngine policyEngine = new RangerPolicyEngineImpl(testName, servicePolicies, policyEngineOptions,  pluginContext, rangerRoles);
 
 		policyEngine.setUseForwardedIPAddress(useForwardedIPAddress);
 		policyEngine.setTrustedProxyAddresses(trustedProxyAddresses);
@@ -464,8 +502,8 @@ public class TestPolicyEngine {
 		if (testCase.updatedPolicies != null) {
 			servicePolicies.setPolicyDeltas(testCase.updatedPolicies.policyDeltas);
 			servicePolicies.setSecurityZones(testCase.updatedPolicies.securityZones);
-			RangerPolicyEngine updatedPolicyEngine = policyEngine.cloneWithDelta(servicePolicies);
-			RangerPolicyEngine updatedPolicyEngineForResourceAccessInfo = policyEngineForResourceAccessInfo.cloneWithDelta(servicePolicies);
+			RangerPolicyEngine updatedPolicyEngine = policyEngine.cloneWithDelta(servicePolicies, rangerRoles);
+			RangerPolicyEngine updatedPolicyEngineForResourceAccessInfo = policyEngineForResourceAccessInfo.cloneWithDelta(servicePolicies, rangerRoles);
 			runTestCaseTests(updatedPolicyEngine, updatedPolicyEngineForResourceAccessInfo, testCase.serviceDef, testName, testCase.updatedTests);
 		}
 	}
diff --git a/knox-agent/src/main/java/org/apache/ranger/admin/client/RangerAdminJersey2RESTClient.java b/knox-agent/src/main/java/org/apache/ranger/admin/client/RangerAdminJersey2RESTClient.java
index 5dcce11..53be9c2 100644
--- a/knox-agent/src/main/java/org/apache/ranger/admin/client/RangerAdminJersey2RESTClient.java
+++ b/knox-agent/src/main/java/org/apache/ranger/admin/client/RangerAdminJersey2RESTClient.java
@@ -206,6 +206,93 @@ public class RangerAdminJersey2RESTClient extends AbstractRangerAdminClient {
 	}
 
 	@Override
+	public RangerRoles getRolesIfUpdated(final long lastKnowRoleVersion, final long lastActivationTimeInMillis) throws Exception {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerAdminJersey2RESTClient.getRolesIfUpdated(" + lastKnowRoleVersion + ", " + lastActivationTimeInMillis + ")");
+		}
+
+		UserGroupInformation user = MiscUtil.getUGILoginUser();
+		boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled();
+
+		String      relativeURL = null;
+		RangerRoles ret         = null;
+		Response    response    = null;
+
+		Map<String, String> queryParams = new HashMap<String, String>();
+		queryParams.put(RangerRESTUtils.REST_PARAM_LAST_KNOWN_ROLE_VERSION, Long.toString(lastKnowRoleVersion));
+		queryParams.put(RangerRESTUtils.REST_PARAM_LAST_ACTIVATION_TIME, Long.toString(lastActivationTimeInMillis));
+		queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, _pluginId);
+		queryParams.put(RangerRESTUtils.REST_PARAM_CLUSTER_NAME, _clusterName);
+
+		if (isSecureMode) {
+			if (LOG.isDebugEnabled()) {
+				LOG.debug("Checking Roles if updated as user : " + user);
+			}
+
+			relativeURL = _utils.getSecureUrlForRoleUpdate(_baseUrl, _serviceName);
+			final String secureRelativeUrl = relativeURL;
+			PrivilegedAction<Response> action = new PrivilegedAction<Response>() {
+				public Response run() {
+					return get(queryParams, secureRelativeUrl);
+				}
+			};
+			response = user.doAs(action);
+		} else {
+			if (LOG.isDebugEnabled()) {
+				LOG.debug("Checking Roles if updated with old api call");
+			}
+
+			relativeURL = _utils.getUrlForRoleUpdate(_baseUrl, _serviceName);
+			response = get(queryParams, relativeURL);
+		}
+
+		int httpResponseCode = response == null ? -1 : response.getStatus();
+		String body = null;
+
+		switch (httpResponseCode) {
+			case 200:
+				body = response.readEntity(String.class);
+
+				if (LOG.isDebugEnabled()) {
+					LOG.debug("Response from 200 server: " + body);
+				}
+
+				Gson gson = getGson();
+				ret = gson.fromJson(body, RangerRoles.class);
+
+				if (LOG.isDebugEnabled()) {
+					LOG.debug("Deserialized response to: " + ret);
+				}
+				break;
+			case 304:
+				LOG.debug("Got response: 304. Ok. Returning null");
+				break;
+			case -1:
+				LOG.warn("Unexpected: Null response from policy server while trying to get policies! Returning null!");
+				break;
+			case 404: {
+				if (response.hasEntity()) {
+					body = response.readEntity(String.class);
+					if (StringUtils.isNotBlank(body)) {
+						RangerServiceNotFoundException.throwExceptionIfServiceNotFound(_serviceName, body);
+					}
+				}
+				LOG.warn("Received 404 error code with body:[" + body + "], Ignoring");
+				break;
+			}
+			default:
+				body = response.readEntity(String.class);
+				LOG.warn(String.format("Unexpected: Received status[%d] with body[%s] form url[%s]", httpResponseCode, body, relativeURL));
+				break;
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerAdminJersey2RESTClient.getRolesIfUpdated(" + lastKnowRoleVersion + ", " + lastActivationTimeInMillis + "): " + ret);
+		}
+		return ret;
+	}
+
+	@Override
 	public void grantAccess(GrantRevokeRequest request) throws Exception {
 
 		if(LOG.isDebugEnabled()) {
diff --git a/security-admin/db/mysql/optimized/current/ranger_core_db_mysql.sql b/security-admin/db/mysql/optimized/current/ranger_core_db_mysql.sql
index 8e42bd9..1857a77 100644
--- a/security-admin/db/mysql/optimized/current/ranger_core_db_mysql.sql
+++ b/security-admin/db/mysql/optimized/current/ranger_core_db_mysql.sql
@@ -1234,6 +1234,8 @@ CREATE TABLE `x_service_version_info` (
 `policy_update_time` datetime NULL DEFAULT NULL,
 `tag_version` bigint(20) NOT NULL DEFAULT 0,
 `tag_update_time` datetime NULL DEFAULT NULL,
+`role_version` bigint(20) NOT NULL DEFAULT 0,
+`role_update_time` datetime NULL DEFAULT NULL,
 primary key (`id`),
 CONSTRAINT `x_service_version_info_FK_service_id` FOREIGN KEY (`service_id`) REFERENCES `x_service` (`id`)
 )ROW_FORMAT=DYNAMIC;
diff --git a/security-admin/db/mysql/patches/043-add-role-version-in-serviceVersionInfo.sql b/security-admin/db/mysql/patches/043-add-role-version-in-serviceVersionInfo.sql
new file mode 100644
index 0000000..def5678
--- /dev/null
+++ b/security-admin/db/mysql/patches/043-add-role-version-in-serviceVersionInfo.sql
@@ -0,0 +1,34 @@
+-- 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.
+
+drop procedure if exists add-role-version-in-serviceVersionInfo;
+
+delimiter ;;
+create procedure add-role-version-in-serviceVersionInfo() begin
+
+if not exists (select * from information_schema.columns where table_schema=database() and table_name = 'x_service_version_info' and column_name='role_version') then
+        ALTER TABLE x_service_version_info ADD role_version bigint(20) NOT NULL DEFAULT 0;
+end if;
+end;;
+
+if not exists (select * from information_schema.columns where table_schema=database() and table_name = 'x_service_version_info' and column_name='role_update_time') then
+        ALTER TABLE x_service_version_info ADD role_update_time datetime NULL DEFAULT NULL;
+end if;
+end;;
+
+delimiter ;
+call add-role-version-in-serviceVersionInfo();
+
+drop procedure if exists add-role-version-in-serviceVersionInfo;
diff --git a/security-admin/db/oracle/optimized/current/ranger_core_db_oracle.sql b/security-admin/db/oracle/optimized/current/ranger_core_db_oracle.sql
index 1b158c9..0293abe 100644
--- a/security-admin/db/oracle/optimized/current/ranger_core_db_oracle.sql
+++ b/security-admin/db/oracle/optimized/current/ranger_core_db_oracle.sql
@@ -1310,6 +1310,8 @@ policy_version NUMBER(20) DEFAULT 0 NOT NULL,
 policy_update_time DATE DEFAULT NULL NULL,
 tag_version NUMBER(20) DEFAULT 0 NOT NULL,
 tag_update_time DATE DEFAULT NULL NULL,
+role_version NUMBER(20) DEFAULT 0 NOT NULL,
+role_update_time DATE DEFAULT NULL NULL,
 primary key (id),
 CONSTRAINT x_svc_ver_info_FK_service_id FOREIGN KEY (service_id) REFERENCES x_service(id)
 );
diff --git a/security-admin/db/oracle/patches/043-add-role-version-in-serviceVersionInfo.sql b/security-admin/db/oracle/patches/043-add-role-version-in-serviceVersionInfo.sql
new file mode 100644
index 0000000..a3fd43d
--- /dev/null
+++ b/security-admin/db/oracle/patches/043-add-role-version-in-serviceVersionInfo.sql
@@ -0,0 +1,28 @@
+-- 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.
+
+DECLARE
+        v_count number:=0;
+BEGIN
+        select count(*) into v_count from user_tab_cols where table_name='X_SERVICE_VERSION_INFO' and column_name='ROLE_VERSION';
+        if (v_count = 0) then
+                execute immediate 'ALTER TABLE x_service_version_info ADD role_version NUMBER(20) DEFAULT 0 NOT NULL';
+        end if;
+
+        select count(*) into v_count from user_tab_cols where table_name='X_SERVICE_VERSION_INFO' and column_name='ROLE_UPDATE_TIME';
+        if (v_count = 0) then
+                execute immediate 'ALTER TABLE x_service_version_info ADD role_update_time DATE DEFAULT NULL NULL';
+        end if;
+END;/
diff --git a/security-admin/db/postgres/optimized/current/ranger_core_db_postgres.sql b/security-admin/db/postgres/optimized/current/ranger_core_db_postgres.sql
index 0034759..1d1a31c 100644
--- a/security-admin/db/postgres/optimized/current/ranger_core_db_postgres.sql
+++ b/security-admin/db/postgres/optimized/current/ranger_core_db_postgres.sql
@@ -1165,6 +1165,8 @@ policy_version bigint NOT NULL DEFAULT '0',
 policy_update_time TIMESTAMP DEFAULT NULL,
 tag_version bigint NOT NULL DEFAULT '0',
 tag_update_time TIMESTAMP DEFAULT NULL,
+role_version bigint NOT NULL DEFAULT '0',
+role_update_time TIMESTAMP DEFAULT NULL,
 primary key (id),
 CONSTRAINT x_service_version_info_service_id FOREIGN KEY (service_id) REFERENCES x_service (id)
 );
diff --git a/security-admin/db/postgres/patches/043-add-role-version-in-serviceVersionInfo.sql b/security-admin/db/postgres/patches/043-add-role-version-in-serviceVersionInfo.sql
new file mode 100644
index 0000000..4801ec3
--- /dev/null
+++ b/security-admin/db/postgres/patches/043-add-role-version-in-serviceVersionInfo.sql
@@ -0,0 +1,36 @@
+-- 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.
+
+-- function add-role-version-in-serviceVersionInfo
+select 'delimiter start';
+CREATE OR REPLACE FUNCTION add-role-version-in-serviceVersionInfo()
+RETURNS void AS $$
+DECLARE
+ v_column_exists integer := 0;
+BEGIN
+ select count(*) into v_column_exists from pg_attribute where attrelid in(select oid from pg_class where relname='x_service_version_info') and attname='role_version';
+ IF v_column_exists = 0 THEN
+  ALTER TABLE x_service_version_info ADD COLUMN role_version bigint NOT NULL DEFAULT '0';
+ END IF;
+  select count(*) into v_column_exists from pg_attribute where attrelid in(select oid from pg_class where relname='x_service_version_info') and attname='role_update_time';
+ IF v_column_exists = 0 THEN
+  ALTER TABLE x_service_version_info ADD COLUMN role_update_time TIMESTAMP DEFAULT NULL;
+ END IF;
+END;
+$$ LANGUAGE plpgsql;
+select 'delimiter end';
+
+select add-role-version-in-serviceVersionInfo();
+select 'delimiter end';
diff --git a/security-admin/db/sqlanywhere/optimized/current/ranger_core_db_sqlanywhere.sql b/security-admin/db/sqlanywhere/optimized/current/ranger_core_db_sqlanywhere.sql
index 9dc7656..5381398 100644
--- a/security-admin/db/sqlanywhere/optimized/current/ranger_core_db_sqlanywhere.sql
+++ b/security-admin/db/sqlanywhere/optimized/current/ranger_core_db_sqlanywhere.sql
@@ -1005,6 +1005,8 @@ CREATE TABLE dbo.x_service_version_info(
 	policy_update_time datetime DEFAULT NULL NULL,
 	tag_version bigint NOT NULL DEFAULT 0,
 	tag_update_time datetime DEFAULT NULL NULL,
+	role_version bigint NOT NULL DEFAULT 0,
+	role_update_time datetime DEFAULT NULL NULL,
 	CONSTRAINT x_service_version_info_PK_id PRIMARY KEY CLUSTERED(id)
 )
 GO
diff --git a/security-admin/db/sqlanywhere/patches/043-add-role-version-in-serviceVersionInfo.sql b/security-admin/db/sqlanywhere/patches/043-add-role-version-in-serviceVersionInfo.sql
new file mode 100644
index 0000000..3862ea3
--- /dev/null
+++ b/security-admin/db/sqlanywhere/patches/043-add-role-version-in-serviceVersionInfo.sql
@@ -0,0 +1,25 @@
+-- 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.
+
+IF NOT EXISTS(select * from SYS.SYSCOLUMNS where tname = 'x_service_version_info' and cname = 'role_version') THEN
+		ALTER TABLE dbo.x_service_version_info ADD role_version bigint NOT NULL DEFAULT 0;
+END IF;
+GO
+IF NOT EXISTS(select * from SYS.SYSCOLUMNS where tname = 'x_service_version_info' and cname = 'role_update_time') THEN
+		ALTER TABLE dbo.x_service_version_info ADD role_update_time datetime DEFAULT NULL NULL;
+END IF;
+GO
+
+exit
diff --git a/security-admin/db/sqlserver/optimized/current/ranger_core_db_sqlserver.sql b/security-admin/db/sqlserver/optimized/current/ranger_core_db_sqlserver.sql
index 9383d1a..d24de68 100644
--- a/security-admin/db/sqlserver/optimized/current/ranger_core_db_sqlserver.sql
+++ b/security-admin/db/sqlserver/optimized/current/ranger_core_db_sqlserver.sql
@@ -2048,6 +2048,8 @@ CREATE TABLE [dbo].[x_service_version_info](
         [policy_update_time] [datetime2] DEFAULT NULL NULL,
         [tag_version] [bigint] NOT NULL DEFAULT 0,
         [tag_update_time] [datetime2] DEFAULT NULL NULL,
+        [policy_version] [bigint] NOT NULL DEFAULT 0,
+        [role_update_time] [datetime2] DEFAULT NULL NULL,
         PRIMARY KEY CLUSTERED
 (
         [id] ASC
diff --git a/security-admin/db/sqlserver/patches/042-add-role-version-in-serviceVersionInfo.sql b/security-admin/db/sqlserver/patches/042-add-role-version-in-serviceVersionInfo.sql
new file mode 100644
index 0000000..4f9b379
--- /dev/null
+++ b/security-admin/db/sqlserver/patches/042-add-role-version-in-serviceVersionInfo.sql
@@ -0,0 +1,29 @@
+-- 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.
+
+GO
+IF NOT EXISTS(select * from INFORMATION_SCHEMA.columns where table_name = 'x_service_version_info' and column_name = 'role_version')
+BEGIN
+	ALTER TABLE [dbo].[x_service_version_info] ADD [policy_version] [bigint] NOT NULL DEFAULT 0;
+END
+GO
+GO
+IF NOT EXISTS(select * from INFORMATION_SCHEMA.columns where table_name = 'x_service_version_info' and column_name = 'role_update_time')
+BEGIN
+	ALTER TABLE [dbo].[x_service_version_info] ADD [role_update_time] [datetime2] DEFAULT NULL NULL;
+END
+GO
+
+exit
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/AssetMgr.java b/security-admin/src/main/java/org/apache/ranger/biz/AssetMgr.java
index 63959c9..9d26fb5 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/AssetMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/AssetMgr.java
@@ -684,24 +684,34 @@ public class AssetMgr extends AssetMgrBase {
 		pluginSvcVersionInfo.setHostName(hostName);
 		pluginSvcVersionInfo.setIpAddress(ipAddress);
 
-		if (entityType == RangerPluginInfo.ENTITY_TYPE_POLICIES) {
-			pluginSvcVersionInfo.setPolicyActiveVersion(lastKnownVersion);
-			pluginSvcVersionInfo.setPolicyActivationTime(lastActivationTime);
-			pluginSvcVersionInfo.setPolicyDownloadedVersion(downloadedVersion);
-			pluginSvcVersionInfo.setPolicyDownloadTime(new Date().getTime());
-		} else {
-			pluginSvcVersionInfo.setTagActiveVersion(lastKnownVersion);
-			pluginSvcVersionInfo.setTagActivationTime(lastActivationTime);
-			pluginSvcVersionInfo.setTagDownloadedVersion(downloadedVersion);
-			pluginSvcVersionInfo.setTagDownloadTime(new Date().getTime());
+		switch (entityType) {
+			case 0:
+				pluginSvcVersionInfo.setPolicyActiveVersion(lastKnownVersion);
+				pluginSvcVersionInfo.setPolicyActivationTime(lastActivationTime);
+				pluginSvcVersionInfo.setPolicyDownloadedVersion(downloadedVersion);
+				pluginSvcVersionInfo.setPolicyDownloadTime(new Date().getTime());
+				break;
+			case 1:
+				pluginSvcVersionInfo.setTagActiveVersion(lastKnownVersion);
+				pluginSvcVersionInfo.setTagActivationTime(lastActivationTime);
+				pluginSvcVersionInfo.setTagDownloadedVersion(downloadedVersion);
+				pluginSvcVersionInfo.setTagDownloadTime(new Date().getTime());
+				break;
+			case 2:
+				pluginSvcVersionInfo.setRoleActiveVersion(lastKnownVersion);
+				pluginSvcVersionInfo.setRoleActivationTime(lastActivationTime);
+				pluginSvcVersionInfo.setRoleDownloadedVersion(downloadedVersion);
+				pluginSvcVersionInfo.setRoleDownloadTime(new Date().getTime());
+				break;
 		}
 
-		createOrUpdatePluginInfo(pluginSvcVersionInfo, entityType == RangerPluginInfo.ENTITY_TYPE_POLICIES, httpCode, clusterName);
+		createOrUpdatePluginInfo(pluginSvcVersionInfo, entityType , httpCode, clusterName);
 	}
 
-	private void createOrUpdatePluginInfo(final RangerPluginInfo pluginInfo, final boolean isPolicyDownloadRequest, final int httpCode, String clusterName) {
+	private void createOrUpdatePluginInfo(final RangerPluginInfo pluginInfo, int entityType, final int httpCode, String clusterName) {
+
 		if (logger.isDebugEnabled()) {
-			logger.debug("==> createOrUpdatePluginInfo(pluginInfo = " + pluginInfo + ", isPolicyDownloadRequest = " + isPolicyDownloadRequest + ", httpCode = " + httpCode + ")");
+			logger.debug("==> createOrUpdatePluginInfo(pluginInfo = " + pluginInfo + ", isPolicyDownloadRequest = " + isPolicyDownloadRequest(entityType) + ", httpCode = " + httpCode + ")");
 		}
 
 		final boolean isTagVersionResetNeeded;
@@ -711,23 +721,33 @@ public class AssetMgr extends AssetMgrBase {
 			// then the TransactionManager will roll-back the changes because the HTTP return code is
 			// HttpServletResponse.SC_NOT_MODIFIED
 
-			if (isPolicyDownloadRequest) {
-				isTagVersionResetNeeded = rangerDaoManager.getXXService().findAssociatedTagService(pluginInfo.getServiceName()) == null;
-			} else {
-				isTagVersionResetNeeded = false;
+			switch (entityType) {
+				case 0:
+					isTagVersionResetNeeded = rangerDaoManager.getXXService().findAssociatedTagService(pluginInfo.getServiceName()) == null;
+					break;
+				case 1:
+					isTagVersionResetNeeded = false;
+					break;
+				case 2:
+					isTagVersionResetNeeded = false;
+					break;
+				default:
+					isTagVersionResetNeeded = false;
+					break;
 			}
 
 			Runnable commitWork = new Runnable() {
 				@Override
 				public void run() {
-					doCreateOrUpdateXXPluginInfo(pluginInfo, isPolicyDownloadRequest, isTagVersionResetNeeded, clusterName);
+					doCreateOrUpdateXXPluginInfo(pluginInfo, entityType, isTagVersionResetNeeded, clusterName);
 				}
 			};
 			activityLogger.commitAfterTransactionComplete(commitWork);
 		} else if (httpCode == HttpServletResponse.SC_NOT_FOUND) {
 			Runnable commitWork;
-			if ((isPolicyDownloadRequest && (pluginInfo.getPolicyActiveVersion() == null || pluginInfo.getPolicyActiveVersion() == -1))
-				|| (!isPolicyDownloadRequest && (pluginInfo.getTagActiveVersion() == null || pluginInfo.getTagActiveVersion() == -1))) {
+			if ((isPolicyDownloadRequest(entityType) && (pluginInfo.getPolicyActiveVersion() == null || pluginInfo.getPolicyActiveVersion() == -1))
+					|| (isTagDownloadRequest(entityType) && (pluginInfo.getTagActiveVersion() == null || pluginInfo.getTagActiveVersion() == -1))
+					|| (isRoleDownloadRequest(entityType) && (pluginInfo.getRoleActiveVersion() == null || pluginInfo.getRoleActiveVersion() == -1))) {
 				commitWork = new Runnable() {
 					@Override
 					public void run() {
@@ -738,7 +758,7 @@ public class AssetMgr extends AssetMgrBase {
 				commitWork = new Runnable() {
 					@Override
 					public void run() {
-						doCreateOrUpdateXXPluginInfo(pluginInfo, isPolicyDownloadRequest, false, clusterName);
+						doCreateOrUpdateXXPluginInfo(pluginInfo, entityType, false, clusterName);
 					}
 				};
 			}
@@ -746,15 +766,15 @@ public class AssetMgr extends AssetMgrBase {
 
 		} else {
 			isTagVersionResetNeeded = false;
-			doCreateOrUpdateXXPluginInfo(pluginInfo, isPolicyDownloadRequest, isTagVersionResetNeeded, clusterName);
+			doCreateOrUpdateXXPluginInfo(pluginInfo, entityType, isTagVersionResetNeeded, clusterName);
 		}
 		if (logger.isDebugEnabled()) {
-			logger.debug("<== createOrUpdatePluginInfo(pluginInfo = " + pluginInfo + ", isPolicyDownloadRequest = " + isPolicyDownloadRequest + ", httpCode = " + httpCode + ")");
+			logger.debug("<== createOrUpdatePluginInfo(pluginInfo = " + pluginInfo + ", isPolicyDownloadRequest = " + isPolicyDownloadRequest(entityType) + ", httpCode = " + httpCode + ")");
 		}
 
 	}
 
-	private XXPluginInfo doCreateOrUpdateXXPluginInfo(RangerPluginInfo pluginInfo, final boolean isPolicyDownloadRequest, final boolean isTagVersionResetNeeded, String clusterName) {
+	private XXPluginInfo doCreateOrUpdateXXPluginInfo(RangerPluginInfo pluginInfo, int entityType, final boolean isTagVersionResetNeeded, String clusterName) {
 		XXPluginInfo ret = null;
 		Map<String, String> infoMap = null;
 
@@ -770,14 +790,21 @@ public class AssetMgr extends AssetMgrBase {
 					pluginInfo.setInfo(infoMap);
 				}
 				// ranger-admin is restarted, plugin contains latest versions and no earlier record for this plug-in client
-				if (isPolicyDownloadRequest) {
+				if (isPolicyDownloadRequest(entityType)) {
 					if (pluginInfo.getPolicyDownloadedVersion() != null && pluginInfo.getPolicyDownloadedVersion().equals(pluginInfo.getPolicyActiveVersion())) {
 						// This is our best guess of when policies may have been downloaded
 						pluginInfo.setPolicyDownloadTime(pluginInfo.getPolicyActivationTime());
 					}
-				} else if (pluginInfo.getTagDownloadedVersion() != null && pluginInfo.getTagDownloadedVersion().equals(pluginInfo.getTagActiveVersion())) {
-					// This is our best guess of when tags may have been downloaded
-					pluginInfo.setTagDownloadTime(pluginInfo.getTagActivationTime());
+				} else if (isTagDownloadRequest(entityType)) {
+					if (pluginInfo.getTagDownloadedVersion() != null && pluginInfo.getTagDownloadedVersion().equals(pluginInfo.getTagActiveVersion())) {
+						// This is our best guess of when tags may have been downloaded
+						pluginInfo.setTagDownloadTime(pluginInfo.getTagActivationTime());
+					}
+				} else {
+					if (pluginInfo.getRoleDownloadTime() != null && pluginInfo.getRoleDownloadedVersion().equals(pluginInfo.getRoleActiveVersion())) {
+						// This is our best guess of when role may have been downloaded
+						pluginInfo.setRoleDownloadTime(pluginInfo.getRoleActivationTime());
+					}
 				}
 
 				xObj = pluginInfoService.populateDBObject(pluginInfo);
@@ -802,7 +829,7 @@ public class AssetMgr extends AssetMgrBase {
 					dbObj.setIpAddress(pluginInfo.getIpAddress());
 					needsUpdating = true;
 				}
-				if (isPolicyDownloadRequest) {
+				if (isPolicyDownloadRequest(entityType)) {
 					if (dbObj.getPolicyDownloadedVersion() == null || !dbObj.getPolicyDownloadedVersion().equals(pluginInfo.getPolicyDownloadedVersion())) {
 						dbObj.setPolicyDownloadedVersion(pluginInfo.getPolicyDownloadedVersion());
 						dbObj.setPolicyDownloadTime(pluginInfo.getPolicyDownloadTime());
@@ -824,7 +851,7 @@ public class AssetMgr extends AssetMgrBase {
 						dbObj.setPolicyActivationTime(lastPolicyActivationTime);
 						needsUpdating = true;
 					}
-				} else {
+				} else if (isTagDownloadRequest(entityType)){
 					if (dbObj.getTagDownloadedVersion() == null || !dbObj.getTagDownloadedVersion().equals(pluginInfo.getTagDownloadedVersion())) {
 						// First download for tags after tag-service is associated with resource-service
 						dbObj.setTagDownloadedVersion(pluginInfo.getTagDownloadedVersion());
@@ -849,6 +876,30 @@ public class AssetMgr extends AssetMgrBase {
 						dbObj.setTagActivationTime(lastTagActivationTime);
 						needsUpdating = true;
 					}
+				} else {
+					if (dbObj.getRoleDownloadedVersion() == null || !dbObj.getRoleDownloadedVersion().equals(pluginInfo.getRoleDownloadedVersion())) {
+						dbObj.setRoleDownloadedVersion(pluginInfo.getRoleDownloadedVersion());
+						dbObj.setRoleDownloadTime(pluginInfo.getRoleDownloadTime());
+						needsUpdating = true;
+					}
+
+					Long lastKnownRoleVersion = pluginInfo.getRoleActiveVersion();
+					Long lastRoleActivationTime = pluginInfo.getRoleActivationTime();
+
+					if (lastKnownRoleVersion != null && lastKnownRoleVersion == -1) {
+						dbObj.setRoleDownloadTime(pluginInfo.getRoleDownloadTime());
+						needsUpdating = true;
+					}
+
+					if (lastKnownRoleVersion != null && lastKnownRoleVersion > 0 && (dbObj.getRoleActiveVersion() == null || !dbObj.getRoleActiveVersion().equals(lastKnownRoleVersion))) {
+						dbObj.setRoleActiveVersion(lastKnownRoleVersion);
+						needsUpdating = true;
+					}
+
+					if (lastRoleActivationTime != null && lastRoleActivationTime > 0 && (dbObj.getRoleActivationTime() == null || !dbObj.getRoleActivationTime().equals(lastRoleActivationTime))) {
+						dbObj.setRoleActivationTime(lastRoleActivationTime);
+						needsUpdating = true;
+					}
 				}
 
 				if (isTagVersionResetNeeded) {
@@ -1178,4 +1229,16 @@ public class AssetMgr extends AssetMgrBase {
 			throw restErrorUtil.createRESTException("Please provide a valid syncSource", MessageEnums.INVALID_INPUT_DATA);
 		}
 	}
+
+	private boolean isPolicyDownloadRequest(int entityType) {
+		return entityType == 0;
+	}
+
+	private boolean isTagDownloadRequest(int entityType) {
+		return entityType == 1;
+	}
+
+	private boolean isRoleDownloadRequest(int entityType) {
+		return entityType == 2;
+	}
 }
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 7a67e9c..9151a72 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
@@ -32,12 +32,14 @@ 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.common.RangerRoleCache;
 import org.apache.ranger.db.RangerDaoManager;
 import org.apache.ranger.entity.*;
 import org.apache.ranger.plugin.model.RangerRole;
 import org.apache.ranger.plugin.store.AbstractPredicateUtil;
 import org.apache.ranger.plugin.store.RolePredicateUtil;
 import org.apache.ranger.plugin.store.RoleStore;
+import org.apache.ranger.plugin.util.RangerRoles;
 import org.apache.ranger.plugin.util.SearchFilter;
 import org.apache.ranger.service.RangerRoleService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -50,6 +52,8 @@ import com.google.gson.GsonBuilder;
 public class RoleDBStore implements RoleStore {
     private static final Log LOG = LogFactory.getLog(RoleDBStore.class);
 
+    private static final String RANGER_ROLE_GLOBAL_STATE_NAME = "RangerRole";
+
     @Autowired
     RangerRoleService roleService;
 
@@ -97,6 +101,8 @@ public class RoleDBStore implements RoleStore {
             throw restErrorUtil.createRESTException("role with name: " + role.getName() + " already exists", MessageEnums.ERROR_DUPLICATE_OBJECT);
         }
 
+        daoMgr.getXXGlobalState().onGlobalAppDataChange(RANGER_ROLE_GLOBAL_STATE_NAME);
+
         RangerRole createdRole = roleService.create(role);
         if (createdRole == null) {
             throw new Exception("Cannot create role:[" + role + "]");
@@ -119,6 +125,8 @@ public class RoleDBStore implements RoleStore {
         Gson gsonBuilder = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z").create();
         RangerRole oldRole = gsonBuilder.fromJson(xxRole.getRoleText(), RangerRole.class);
 
+        daoMgr.getXXGlobalState().onGlobalAppDataChange(RANGER_ROLE_GLOBAL_STATE_NAME);
+
         RangerRole updatedRole = roleService.update(role);
         if (updatedRole == null) {
             throw new Exception("Cannot update role:[" + role + "]");
@@ -128,6 +136,8 @@ public class RoleDBStore implements RoleStore {
 
         roleService.updatePolicyVersions(updatedRole.getId());
 
+        roleService.updateRoleVersions(updatedRole.getId());
+
         List<XXTrxLog> trxLogList = roleService.getTransactionLog(updatedRole, oldRole, "update");
         bizUtil.createTrxLog(trxLogList);
         return role;
@@ -139,6 +149,9 @@ public class RoleDBStore implements RoleStore {
         if (xxRole == null) {
             throw restErrorUtil.createRESTException("Role with name: " + roleName + " does not exist");
         }
+
+        daoMgr.getXXGlobalState().onGlobalAppDataChange(RANGER_ROLE_GLOBAL_STATE_NAME);
+
         RangerRole role = roleService.read(xxRole.getId());
         roleRefUpdater.cleanupRefTables(role);
         roleService.delete(role);
@@ -151,6 +164,8 @@ public class RoleDBStore implements RoleStore {
     public void deleteRole(Long roleId) throws Exception {
         RangerRole role = roleService.read(roleId);
 
+        daoMgr.getXXGlobalState().onGlobalAppDataChange(RANGER_ROLE_GLOBAL_STATE_NAME);
+
         roleRefUpdater.cleanupRefTables(role);
         roleService.delete(role);
         List<XXTrxLog> trxLogList = roleService.getTransactionLog(role, null, "delete");
@@ -262,5 +277,29 @@ public class RoleDBStore implements RoleStore {
         return service == null ? ListUtils.EMPTY_LIST : getRoles(service.getId());
     }
 
+    public RangerRoles getRangerRoles(String serviceName, Long lastKnownRoleVersion) throws Exception {
+        RangerRoles ret                   = null;
+        Long        rangerRoleVersionInDB = getRoleVersion(serviceName);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("==> RoleDBStore.getRangerRoles() lastKnownRoleVersion= " + lastKnownRoleVersion + " rangerRoleVersionInDB= " + rangerRoleVersionInDB);
+        }
+
+        if (rangerRoleVersionInDB != null) {
+            ret = RangerRoleCache.getInstance().getLatestRangerRoleOrCached(serviceName, this, lastKnownRoleVersion, rangerRoleVersionInDB);
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<= RoleDBStore.getRangerRoles() lastKnownRoleVersion= " + lastKnownRoleVersion + " rangerRoleVersionInDB= " + rangerRoleVersionInDB + " RangerRoles= " + ret);
+        }
+
+        return ret;
+    }
+
+    public Long getRoleVersion(String serviceName) {
+        XXServiceVersionInfo xxServiceVersionInfo =  daoMgr.getXXServiceVersionInfo().findByServiceName(serviceName);
+        return (xxServiceVersionInfo != null) ? xxServiceVersionInfo.getRoleVersion():null;
+    }
+
 }
 
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java b/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java
index 92436ac..85db577 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java
@@ -68,7 +68,6 @@ import org.apache.ranger.common.RangerCommonEnums;
 import org.apache.ranger.common.db.RangerTransactionSynchronizationAdapter;
 import org.apache.ranger.db.XXPolicyDao;
 import org.apache.ranger.entity.XXTagChangeLog;
-import org.apache.ranger.plugin.model.RangerRole;
 import org.apache.ranger.plugin.model.RangerSecurityZone;
 import org.apache.ranger.plugin.util.ServiceTags;
 import org.apache.ranger.plugin.model.validation.RangerServiceDefValidator;
@@ -123,7 +122,6 @@ import org.apache.ranger.entity.XXPolicyRefAccessType;
 import org.apache.ranger.entity.XXPolicyRefCondition;
 import org.apache.ranger.entity.XXPolicyRefResource;
 import org.apache.ranger.entity.XXResourceDef;
-import org.apache.ranger.entity.XXRoleRefRole;
 import org.apache.ranger.entity.XXSecurityZone;
 import org.apache.ranger.entity.XXService;
 import org.apache.ranger.entity.XXServiceConfigDef;
@@ -2812,25 +2810,6 @@ public class ServiceDBStore extends AbstractServiceStore {
 			ret.setTagPolicies(tagPolicies);
 		}
 
-		if (ret != null) {
-			// Add role mapping
-			List<RangerRole> rolesForService = roleStore.getRoles(serviceDbObj.getId());
-			if (CollectionUtils.isNotEmpty(rolesForService)) {
-				Map<String, Set<String>> userRoleMapping = new HashMap<>();
-				Map<String, Set<String>> groupRoleMapping = new HashMap<>();
-				for (RangerRole role : rolesForService) {
-					// Get a closure set of all contained roles too
-					Set<RangerRole> containedRoles = getAllContainedRoles(role);
-
-					buildMap(userRoleMapping, role, containedRoles, true);
-					buildMap(groupRoleMapping, role, containedRoles, false);
-				}
-
-				ret.setUserRoles(userRoleMapping);
-				ret.setGroupRoles(groupRoleMapping);
-			}
-		}
-
 		if (LOG.isDebugEnabled()) {
 			LOG.debug("<== ServiceDBStore.getServicePolicies(" + serviceName + ", " + lastKnownVersion + "): count=" + ((ret == null || ret.getPolicies() == null) ? 0 : ret.getPolicies().size()) + ", delta-count=" + ((ret == null || ret.getPolicyDeltas() == null) ? 0 : ret.getPolicyDeltas().size()));
 		}
@@ -2838,45 +2817,6 @@ public class ServiceDBStore extends AbstractServiceStore {
 		return ret;
 	}
 
-	private Set<RangerRole> getAllContainedRoles(RangerRole role) {
-		Set<RangerRole> allRoles = new HashSet<>();
-		allRoles.add(role);
-		addContainedRoles(allRoles, role);
-		return allRoles;
-	}
-
-	private void addContainedRoles(Set<RangerRole> allRoles, RangerRole role) {
-		List<XXRoleRefRole> roles = daoMgr.getXXRoleRefRole().findByRoleId(role.getId());
-		for (XXRoleRefRole xRefRole : roles) {
-			RangerRole containedRole = roleService.read(xRefRole.getSubRoleId());
-			if (!allRoles.contains(containedRole)) {
-				allRoles.add(containedRole);
-				addContainedRoles(allRoles, containedRole);
-			}
-		}
-	}
-
-	private void buildMap(Map<String, Set<String>> map, RangerRole role, Set<RangerRole> containedRoles, boolean isUser) {
-		buildMap(map, role, role.getName(), isUser);
-
-		for (RangerRole containedRole : containedRoles) {
-			buildMap(map, containedRole, role.getName(), isUser);
-		}
-	}
-
-	private void buildMap(Map<String, Set<String>> map, RangerRole role, String roleName, boolean isUser) {
-		for (RangerRole.RoleMember userOrGroup : isUser ? role.getUsers() : role.getGroups()) {
-			if (StringUtils.isNotEmpty(userOrGroup.getName())) {
-				Set<String> roleNames = map.get(userOrGroup.getName());
-				if (roleNames == null) {
-					roleNames = new HashSet<>();
-					map.put(userOrGroup.getName(), roleNames);
-				}
-				roleNames.add(roleName);
-			}
-		}
-	}
-
 	private static class RangerPolicyDeltaComparator implements Comparator<RangerPolicyDelta>, java.io.Serializable {
 		@Override
 		public int compare(RangerPolicyDelta me, RangerPolicyDelta other) {
@@ -3353,7 +3293,7 @@ public class ServiceDBStore extends AbstractServiceStore {
 		updatePolicyVersion(service, policyDeltaType, policy);
 	}
 
-	public enum VERSION_TYPE { POLICY_VERSION, TAG_VERSION, POLICY_AND_TAG_VERSION }
+	public enum VERSION_TYPE { POLICY_VERSION, TAG_VERSION, POLICY_AND_TAG_VERSION, ROLE_VERSION }
 
 	private void updatePolicyVersion(RangerService service, Integer policyDeltaType, RangerPolicy policy) throws Exception {
 		if(service == null || service.getId() == null) {
@@ -3419,6 +3359,13 @@ public class ServiceDBStore extends AbstractServiceStore {
 				serviceVersionInfoDbObj.setTagUpdateTime(now);
 			}
 
+			if (versionType == VERSION_TYPE.ROLE_VERSION) {
+				// get the LatestRoleVersion from the GlobalTable and update ServiceInfo for a service
+				Long currentRoleVersion = daoMgr.getXXGlobalState().getRoleVersion("RangerRole");
+				serviceVersionInfoDbObj.setRolVersion(currentRoleVersion);
+				serviceVersionInfoDbObj.setRoleUpdateTime(now);
+			}
+
 			serviceVersionInfoDao.update(serviceVersionInfoDbObj);
 
 		} else {
diff --git a/security-admin/src/main/java/org/apache/ranger/common/RangerRoleCache.java b/security-admin/src/main/java/org/apache/ranger/common/RangerRoleCache.java
new file mode 100644
index 0000000..b0bd427
--- /dev/null
+++ b/security-admin/src/main/java/org/apache/ranger/common/RangerRoleCache.java
@@ -0,0 +1,142 @@
+/*
+ * 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.ranger.common;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.authorization.hadoop.config.RangerConfiguration;
+import org.apache.ranger.biz.RoleDBStore;
+import org.apache.ranger.plugin.model.RangerRole;
+
+import org.apache.ranger.plugin.util.RangerRoles;
+import org.apache.ranger.plugin.util.SearchFilter;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class RangerRoleCache {
+	private static final Log LOG = LogFactory.getLog(RangerRoleCache.class);
+
+	private static final int MAX_WAIT_TIME_FOR_UPDATE = 10;
+
+	public static volatile RangerRoleCache sInstance = null;
+	private final int waitTimeInSeconds;
+	final ReentrantLock lock = new ReentrantLock();
+
+	RangerRoleCacheWrapper rangerRoleCacheWrapper = null;
+
+	public static RangerRoleCache getInstance() {
+		if (sInstance == null) {
+			synchronized (RangerRoleCache.class) {
+				if (sInstance == null) {
+					sInstance = new RangerRoleCache();
+				}
+			}
+		}
+		return sInstance;
+	}
+
+	private RangerRoleCache() {
+		waitTimeInSeconds = RangerConfiguration.getInstance().getInt("ranger.admin.policy.download.cache.max.waittime.for.update", MAX_WAIT_TIME_FOR_UPDATE);
+	}
+
+	public RangerRoles getLatestRangerRoleOrCached(String serviceName, RoleDBStore roleDBStore, Long lastKnownRoleVersion, Long rangerRoleVersionInDB) throws Exception {
+		RangerRoles ret = null;
+
+		if (lastKnownRoleVersion == null || !lastKnownRoleVersion.equals(rangerRoleVersionInDB)) {
+			rangerRoleCacheWrapper = new RangerRoleCacheWrapper();
+			ret = rangerRoleCacheWrapper.getLatestRangerRoles(serviceName, roleDBStore, lastKnownRoleVersion, rangerRoleVersionInDB);
+		} else if (lastKnownRoleVersion.equals(rangerRoleVersionInDB)) {
+			ret = null;
+		} else {
+			ret = rangerRoleCacheWrapper.getRangerRoles();
+		}
+
+		return ret;
+	}
+
+	private class RangerRoleCacheWrapper {
+		RangerRoles rangerRoles;
+		Long 			rangerRoleVersion;
+
+		RangerRoleCacheWrapper() {
+			this.rangerRoles = null;
+			this.rangerRoleVersion = -1L;
+		}
+
+		public RangerRoles getRangerRoles() {
+			return this.rangerRoles;
+		}
+
+		public Long getRangerRoleVersion() {
+			return this.rangerRoleVersion;
+		}
+
+		public RangerRoles getLatestRangerRoles(String serviceName, RoleDBStore roleDBStore, Long lastKnownRoleVersion, Long rangerRoleVersionInDB) throws Exception {
+			RangerRoles ret	 = null;
+			boolean         lockResult   = false;
+			if (LOG.isDebugEnabled()) {
+				LOG.debug("==> RangerRoleCache.getLatestRangerRoles(ServiceName= " + serviceName + " lastKnownRoleVersion= " + lastKnownRoleVersion + " rangerRoleVersionInDB= " + rangerRoleVersionInDB + ")");
+			}
+
+			try {
+				lockResult = lock.tryLock(waitTimeInSeconds, TimeUnit.SECONDS);
+
+				if (lockResult) {
+					// We are getting all the Roles to be downloaded for now. Should do downloades for each service based on what roles are there in the policies.
+					SearchFilter searchFilter = null;
+					final Set<RangerRole> rangerRoleInDB = new HashSet<>(roleDBStore.getRoles(searchFilter));
+
+					Date updateTime = new Date();
+
+					if (rangerRoleInDB != null) {
+						ret = new RangerRoles();
+						ret.setRangerRoles(rangerRoleInDB);
+						ret.setRoleUpdateTime(updateTime);
+						ret.setRoleVersion(rangerRoleVersionInDB);
+						rangerRoleVersion = rangerRoleVersionInDB;
+					} else {
+						LOG.error("Could not get Ranger Roles from database ...");
+					}
+				} else {
+					if (LOG.isDebugEnabled()) {
+						LOG.debug("Could not get lock in [" + waitTimeInSeconds + "] seconds, returning cached RangerRoles");
+					}
+					ret = getRangerRoles();
+				}
+			} catch (InterruptedException exception) {
+				LOG.error("RangerRoleCache.getLatestRangerRoles:lock got interrupted..", exception);
+			} finally {
+				if (lockResult) {
+					lock.unlock();
+				}
+			}
+
+			if (LOG.isDebugEnabled()) {
+				LOG.debug("<== RangerRoleCache.getLatestRangerRoles(ServiceName= " + serviceName + " lastKnownRoleVersion= " + lastKnownRoleVersion + " rangerRoleVersionInDB= " + rangerRoleVersionInDB + " RangerRoles= " + ret + ")");
+			}
+			return ret;
+		}
+	}
+}
+
diff --git a/security-admin/src/main/java/org/apache/ranger/db/XXGlobalStateDao.java b/security-admin/src/main/java/org/apache/ranger/db/XXGlobalStateDao.java
index f6b9e1a..d687e73 100644
--- a/security-admin/src/main/java/org/apache/ranger/db/XXGlobalStateDao.java
+++ b/security-admin/src/main/java/org/apache/ranger/db/XXGlobalStateDao.java
@@ -17,6 +17,7 @@
 
 package org.apache.ranger.db;
 
+import com.google.gson.Gson;
 import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
 import org.apache.ranger.common.DateUtil;
@@ -26,11 +27,15 @@ import org.springframework.stereotype.Service;
 
 import javax.persistence.NoResultException;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
 
 @Service
 public class XXGlobalStateDao extends BaseDao<XXGlobalState> {
     private static final Logger logger = Logger.getLogger(RangerDaoManager.class);
 
+    final static String RANGER_ROLE_VERSION_LABEL = "RangerRoleVersion";
+
     public void onGlobalStateChange(String stateName) throws Exception {
 
         if (StringUtils.isBlank(stateName)) {
@@ -58,6 +63,48 @@ public class XXGlobalStateDao extends BaseDao<XXGlobalState> {
             }
         }
     }
+
+    public void onGlobalAppDataChange(String stateName) throws Exception {
+
+        if (StringUtils.isBlank(stateName)) {
+            logger.error("Invalid name for state:[" + stateName +"]");
+            throw new Exception("Invalid name for state:[" + stateName +"]");
+        } else {
+            try {
+                XXGlobalState globalState = findByStateName(stateName);
+                if (globalState == null) {
+                    globalState = new XXGlobalState();
+                    globalState.setStateName(stateName);
+                    Map<String,String> roleVersion = new HashMap<>();
+                    roleVersion.put(RANGER_ROLE_VERSION_LABEL,new String(Long.toString(1L)));
+                    globalState.setAppData(new Gson().toJson(roleVersion));
+                    create(globalState);
+                } else {
+                    Map<String,String> roleVersionJson = new Gson().fromJson(globalState.getAppData(),Map.class);
+                    Long               roleVersion     = Long.valueOf(roleVersionJson.get(RANGER_ROLE_VERSION_LABEL)) + 1L;
+                    roleVersionJson.put(RANGER_ROLE_VERSION_LABEL,new String(Long.toString(roleVersion)));
+                    globalState.setAppData(new Gson().toJson(roleVersionJson));
+                    update(globalState);
+                }
+            } catch (Exception exception) {
+                logger.error("Cannot create/update GlobalState for state:[" + stateName + "]", exception);
+                throw exception;
+            }
+        }
+    }
+
+    public Long getRoleVersion(String stateName) {
+        Long ret = null;
+        try {
+            XXGlobalState       globalState     = findByStateName(stateName);
+            Map<String, String> roleVersionJson = new Gson().fromJson(globalState.getAppData(), Map.class);
+            ret                                 = Long.valueOf(roleVersionJson.get(RANGER_ROLE_VERSION_LABEL));
+        } catch (Exception exception) {
+            logger.warn("Unable to find the role version in Ranger Database");
+        }
+        return ret;
+    }
+
     /**
      * Default Constructor
      */
diff --git a/security-admin/src/main/java/org/apache/ranger/entity/XXServiceVersionInfo.java b/security-admin/src/main/java/org/apache/ranger/entity/XXServiceVersionInfo.java
index cef3863..1d81337 100644
--- a/security-admin/src/main/java/org/apache/ranger/entity/XXServiceVersionInfo.java
+++ b/security-admin/src/main/java/org/apache/ranger/entity/XXServiceVersionInfo.java
@@ -68,6 +68,13 @@ public class XXServiceVersionInfo implements java.io.Serializable {
 	@Column(name="tag_update_time"   )
 	protected Date tagUpdateTime = DateUtil.getUTCDate();
 
+	@Column(name = "role_version")
+	protected Long roleVersion;
+
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="role_update_time"   )
+	protected Date roleUpdateTime = DateUtil.getUTCDate();
+
 	/**
 	 * Default constructor. This will set all the attributes to default value.
 	 */
@@ -130,6 +137,21 @@ public class XXServiceVersionInfo implements java.io.Serializable {
 		return this.tagUpdateTime;
 	}
 
+	public void setRolVersion(Long roleVersion) {
+		this.roleVersion = roleVersion;
+	}
+
+	public Long getRoleVersion() {
+		return this.roleVersion;
+	}
+
+	public void setRoleUpdateTime( Date updateTime ) {
+		this.roleUpdateTime = updateTime;
+	}
+
+	public Date getRoleUpdateTime( ) {
+		return this.roleUpdateTime;
+	}
 
 	/**
 	 * This return the bean content in string format
diff --git a/security-admin/src/main/java/org/apache/ranger/rest/RoleREST.java b/security-admin/src/main/java/org/apache/ranger/rest/RoleREST.java
index 4af768a..d28cf3d 100644
--- a/security-admin/src/main/java/org/apache/ranger/rest/RoleREST.java
+++ b/security-admin/src/main/java/org/apache/ranger/rest/RoleREST.java
@@ -27,6 +27,7 @@ import java.util.List;
 import java.util.Set;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.*;
 import javax.ws.rs.core.Context;
 
@@ -36,6 +37,7 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.ranger.admin.client.datatype.RESTResponse;
 import org.apache.ranger.authorization.utils.StringUtil;
+import org.apache.ranger.biz.AssetMgr;
 import org.apache.ranger.biz.RangerBizUtil;
 import org.apache.ranger.biz.RoleDBStore;
 import org.apache.ranger.biz.ServiceDBStore;
@@ -43,14 +45,21 @@ import org.apache.ranger.biz.XUserMgr;
 import org.apache.ranger.common.RESTErrorUtil;
 import org.apache.ranger.common.RangerSearchUtil;
 import org.apache.ranger.common.RangerValidatorFactory;
+import org.apache.ranger.common.ServiceUtil;
 import org.apache.ranger.common.UserSessionBase;
 import org.apache.ranger.common.RangerConstants;
 import org.apache.ranger.common.PropertiesUtil;
 import org.apache.ranger.common.ContextUtil;
+import org.apache.ranger.db.RangerDaoManager;
+import org.apache.ranger.entity.XXService;
+import org.apache.ranger.entity.XXServiceDef;
+import org.apache.ranger.plugin.model.RangerPluginInfo;
 import org.apache.ranger.plugin.model.RangerRole;
 import org.apache.ranger.plugin.model.RangerService;
 import org.apache.ranger.plugin.policyengine.RangerPolicyEngine;
+import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil;
 import org.apache.ranger.plugin.util.GrantRevokeRoleRequest;
+import org.apache.ranger.plugin.util.RangerRoles;
 import org.apache.ranger.plugin.util.SearchFilter;
 import org.apache.ranger.service.RangerRoleService;
 import org.apache.ranger.service.XUserService;
@@ -71,10 +80,18 @@ public class RoleREST {
 
     private static List<String> INVALID_USERS = new ArrayList<>();
 
+    public static final String Allowed_User_List_For_Download = "policy.download.auth.users";
+
     @Autowired
     RESTErrorUtil restErrorUtil;
 
     @Autowired
+    AssetMgr assetMgr;
+
+    @Autowired
+    RangerDaoManager daoManager;
+
+    @Autowired
     RoleDBStore roleStore;
 
     @Autowired
@@ -90,6 +107,9 @@ public class RoleREST {
     RangerSearchUtil searchUtil;
 
     @Autowired
+    ServiceUtil serviceUtil;
+
+    @Autowired
     RangerValidatorFactory validatorFactory;
 
     @Autowired
@@ -658,6 +678,175 @@ public class RoleREST {
         return new ArrayList<>(ret);
     }
 
+    @GET
+    @Path("/download/{serviceName}")
+    @Produces({ "application/json", "application/xml" })
+    public RangerRoles getRangerRolesIfUpdated(
+            @PathParam("serviceName") String serviceName,
+            @QueryParam("lastKnownRoleVersion") Long lastKnownRoleVersion,
+            @DefaultValue("0") @QueryParam("lastActivationTime") Long lastActivationTime,
+            @QueryParam("pluginId") String pluginId,
+            @DefaultValue("") @QueryParam("clusterName") String clusterName,
+            @Context HttpServletRequest request) throws Exception {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("==> RoleREST.getRangerRolesIfUpdated("
+                    + serviceName + ", " + lastKnownRoleVersion + ", " + lastActivationTime + ")");
+        }
+        RangerRoles ret = null;
+
+        boolean isValid           = false;
+        int     httpCode          = HttpServletResponse.SC_OK;
+        Long    downloadedVersion = null;
+        String  logMsg            = null;
+
+        try {
+            isValid = serviceUtil.isValidService(serviceName, request);
+        } catch (WebApplicationException webException) {
+            httpCode = webException.getResponse().getStatus();
+            logMsg = webException.getResponse().getEntity().toString();
+        } catch (Exception e) {
+            httpCode = HttpServletResponse.SC_BAD_REQUEST;
+            logMsg = e.getMessage();
+        }
+        if (isValid) {
+            if (lastKnownRoleVersion == null) {
+                lastKnownRoleVersion = Long.valueOf(-1);
+            }
+            try {
+                RangerRoles rangerRoles = roleStore.getRangerRoles(serviceName, lastKnownRoleVersion);
+                if (rangerRoles == null) {
+                    downloadedVersion = lastKnownRoleVersion;
+                    httpCode = HttpServletResponse.SC_NOT_MODIFIED;
+                    logMsg = "No change since last update";
+                } else {
+                    downloadedVersion = rangerRoles.getRoleVersion();
+                    rangerRoles.setServiceName(serviceName);
+                    ret = rangerRoles;
+                    httpCode = HttpServletResponse.SC_OK;
+                    logMsg = "Returning RangerRoles =>" + (ret.toString());
+                }
+
+            } catch (Throwable excp) {
+                LOG.error("getRangerRolesIfUpdated(" + serviceName + ", " + lastKnownRoleVersion + ", " + lastActivationTime + ") failed", excp);
+                httpCode = HttpServletResponse.SC_BAD_REQUEST;
+                logMsg = excp.getMessage();
+            }
+        }
+
+        assetMgr.createPluginInfo(serviceName, pluginId, request, RangerPluginInfo.ENTITY_TYPE_ROLES, downloadedVersion, lastKnownRoleVersion, lastActivationTime, httpCode, clusterName);
+
+        if (httpCode != HttpServletResponse.SC_OK) {
+            boolean logError = httpCode != HttpServletResponse.SC_NOT_MODIFIED;
+            throw restErrorUtil.createRESTException(httpCode, logMsg, logError);
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== RoleREST.getRangerRolesIfUpdated(" + serviceName + ", " + lastKnownRoleVersion + ", " + lastActivationTime + ")" + ret);
+        }
+        return ret;
+    }
+
+    @GET
+    @Path("/secure/download/{serviceName}")
+    @Produces({ "application/json", "application/xml" })
+    public RangerRoles getSecureRangerRolesIfUpdated(
+            @PathParam("serviceName") String serviceName,
+            @QueryParam("lastKnownRoleVersion") Long lastKnownRoleVersion,
+            @DefaultValue("0") @QueryParam("lastActivationTime") Long lastActivationTime,
+            @QueryParam("pluginId") String pluginId,
+            @DefaultValue("") @QueryParam("clusterName") String clusterName,
+            @Context HttpServletRequest request) throws Exception {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("==> RoleREST.getSecureRangerRolesIfUpdated("
+                    + serviceName + ", " + lastKnownRoleVersion + ", " + lastKnownRoleVersion + ")");
+        }
+        RangerRoles ret  = null;
+        int     httpCode          = HttpServletResponse.SC_OK;
+        String  logMsg            = null;
+        boolean isAllowed         = false;
+        boolean isAdmin           = bizUtil.isAdmin();
+        boolean isKeyAdmin        = bizUtil.isKeyAdmin();
+        Long    downloadedVersion = null;
+
+        request.setAttribute("downloadPolicy", "secure");
+
+        boolean isValid = false;
+        try {
+            isValid = serviceUtil.isValidService(serviceName, request);
+        } catch (WebApplicationException webException) {
+            httpCode = webException.getResponse().getStatus();
+            logMsg = webException.getResponse().getEntity().toString();
+        } catch (Exception e) {
+            httpCode = HttpServletResponse.SC_BAD_REQUEST;
+            logMsg = e.getMessage();
+        }
+        if (isValid) {
+            if (lastKnownRoleVersion == null) {
+                lastKnownRoleVersion = Long.valueOf(-1);
+            }
+            try {
+                XXService xService = daoManager.getXXService().findByName(serviceName);
+                if (xService == null) {
+                    LOG.error("Requested Service not found. serviceName=" + serviceName);
+                    throw restErrorUtil.createRESTException(HttpServletResponse.SC_NOT_FOUND, "Service:" + serviceName + " not found",
+                            false);
+                }
+                XXServiceDef xServiceDef = daoManager.getXXServiceDef().getById(xService.getType());
+                RangerService rangerService = svcStore.getServiceByName(serviceName);
+
+                if (org.apache.commons.lang.StringUtils.equals(xServiceDef.getImplclassname(), EmbeddedServiceDefsUtil.KMS_IMPL_CLASS_NAME)) {
+                    if (isKeyAdmin) {
+                        isAllowed = true;
+                    }else {
+                        isAllowed = bizUtil.isUserAllowed(rangerService, Allowed_User_List_For_Download);
+                    }
+                }else{
+                    if (isAdmin) {
+                        isAllowed = true;
+                    }else{
+                        isAllowed = bizUtil.isUserAllowed(rangerService, Allowed_User_List_For_Download);
+                    }
+                }
+
+                if (isAllowed) {
+                    RangerRoles rangerRoles = roleStore.getRangerRoles(serviceName, lastKnownRoleVersion);
+                    if (rangerRoles == null) {
+                        downloadedVersion = lastKnownRoleVersion;
+                        httpCode = HttpServletResponse.SC_NOT_MODIFIED;
+                        logMsg = "No change since last update";
+                    } else {
+                        downloadedVersion = rangerRoles.getRoleVersion();
+                        rangerRoles.setServiceName(serviceName);
+                        ret = rangerRoles;
+                        httpCode = HttpServletResponse.SC_OK;
+                        logMsg = "Returning RangerRoles =>" + (ret.toString());
+                    }
+                } else {
+                    LOG.error("getSecureRangerRolesIfUpdated(" + serviceName + ", " + lastKnownRoleVersion + ") failed as User doesn't have permission to UserGroupRoles");
+                    httpCode = HttpServletResponse.SC_UNAUTHORIZED;
+                    logMsg = "User doesn't have permission to download UserGroupRoles";
+                }
+
+            } catch (Throwable excp) {
+                LOG.error("getSecureRangerRolesIfUpdated(" + serviceName + ", " + lastKnownRoleVersion + ", " + lastActivationTime + ") failed", excp);
+                httpCode = HttpServletResponse.SC_BAD_REQUEST;
+                logMsg = excp.getMessage();
+            }
+        }
+
+        assetMgr.createPluginInfo(serviceName, pluginId, request, RangerPluginInfo.ENTITY_TYPE_ROLES, downloadedVersion, lastKnownRoleVersion, lastActivationTime, httpCode, clusterName);
+
+        if (httpCode != HttpServletResponse.SC_OK) {
+            boolean logError = httpCode != HttpServletResponse.SC_NOT_MODIFIED;
+            throw restErrorUtil.createRESTException(httpCode, logMsg, logError);
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== RoleREST.getSecureRangerRolesIfUpdated(" + serviceName + ", " + lastKnownRoleVersion + ", " + lastActivationTime + ")" + ret);
+        }
+        return ret;
+    }
+
     private void ensureAdminAccess(String serviceName, String userName) throws Exception {
 
         /* If userName (execUser) is not same as logged in user then check
diff --git a/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java b/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java
index cfeaadd..190c6f5 100644
--- a/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java
+++ b/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java
@@ -3811,8 +3811,6 @@ public class ServiceREST {
 				ret.setPolicies(servicePolicies.getPolicies());
 				ret.setTagPolicies(servicePolicies.getTagPolicies());
 				ret.setSecurityZones(servicePolicies.getSecurityZones());
-				ret.setUserRoles(servicePolicies.getUserRoles());
-				ret.setGroupRoles(servicePolicies.getGroupRoles());
 
 				if (containsDisabledResourcePolicies) {
 					List<RangerPolicy> filteredPolicies = new ArrayList<RangerPolicy>();
diff --git a/security-admin/src/main/java/org/apache/ranger/service/RangerRoleService.java b/security-admin/src/main/java/org/apache/ranger/service/RangerRoleService.java
index 8857afd..e168278 100644
--- a/security-admin/src/main/java/org/apache/ranger/service/RangerRoleService.java
+++ b/security-admin/src/main/java/org/apache/ranger/service/RangerRoleService.java
@@ -285,6 +285,23 @@ public class RangerRoleService extends RangerRoleServiceBase<XXRole, RangerRole>
         return ret;
     }
 
+    public void updateRoleVersions(Long roleId) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("==> updateRoleVersions(roleId=" + roleId + ")");
+        }
+        // Get all roles which include this role because change to this affects all these roles
+        Set<Long> containingRoles = getContainingRoles(roleId);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("All containing Roles for roleId:[" + roleId +"] are [" + containingRoles + "]");
+        }
+
+        updateRoleVersions(containingRoles);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("<== updateRoleVersions(roleId=" + roleId + ")");
+        }
+    }
 
     private void addContainingRoles(Long roleId, Set<Long> allRoles) {
         if (logger.isDebugEnabled()) {
@@ -330,5 +347,31 @@ public class RangerRoleService extends RangerRoleServiceBase<XXRole, RangerRole>
         }
     }
 
+    private void updateRoleVersions(Set<Long> roleIds) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("==> updatePolicyVersions(roleIds=" + roleIds + ")");
+        }
+
+        if (CollectionUtils.isNotEmpty(roleIds)) {
+            Set<Long> allAffectedServiceIds = new HashSet<>();
+
+            for (Long roleId : roleIds) {
+                List<Long> affectedServiceIds = daoMgr.getXXPolicy().findServiceIdsByRoleId(roleId);
+                allAffectedServiceIds.addAll(affectedServiceIds);
+            }
+
+            if (CollectionUtils.isNotEmpty(allAffectedServiceIds)) {
+                for (final Long serviceId : allAffectedServiceIds) {
+                    Runnable serviceVersionUpdater = new ServiceDBStore.ServiceVersionUpdater(daoMgr, serviceId, ServiceDBStore.VERSION_TYPE.ROLE_VERSION, null, RangerPolicyDelta.CHANGE_TYPE_ROLE_UPDATE, null);
+                    daoMgr.getRangerTransactionSynchronizationAdapter().executeOnTransactionCommit(serviceVersionUpdater);
+                }
+            }
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("<== updatePolicyVersions(roleIds=" + roleIds + ")");
+        }
+    }
+
 }