You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by sp...@apache.org on 2020/02/13 17:21:57 UTC

[ranger] branch master updated: RANGER-2697: Usersync and Ranger admin changes to support retriving additional user/group attributes from LDAP/AD

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

spolavarapu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/master by this push:
     new dc63813  RANGER-2697: Usersync and Ranger admin changes to support retriving additional user/group attributes from LDAP/AD
dc63813 is described below

commit dc6381372bc49bfdfe8586f24102b7d3c376ca58
Author: Sailaja Polavarapu <sp...@cloudera.com>
AuthorDate: Thu Feb 13 09:21:35 2020 -0800

    RANGER-2697: Usersync and Ranger admin changes to support retriving additional user/group attributes from LDAP/AD
---
 .../org/apache/ranger/plugin/model/GroupInfo.java  |  87 ++++++++++
 .../ranger/plugin/model/RangerPluginInfo.java      |  56 ++++++-
 .../org/apache/ranger/plugin/model/UserInfo.java   |  95 +++++++++++
 .../apache/ranger/plugin/util/RangerUserStore.java | 186 +++++++++++++++++++++
 .../ranger/plugin/util/RangerUserStoreUtil.java    |  60 +++++++
 .../main/java/org/apache/ranger/biz/AssetMgr.java  |  49 +++++-
 .../java/org/apache/ranger/biz/RoleDBStore.java    |   2 +-
 .../java/org/apache/ranger/biz/ServiceDBStore.java |   2 +-
 .../main/java/org/apache/ranger/biz/UserMgr.java   |   3 +
 .../main/java/org/apache/ranger/biz/XUserMgr.java  |  59 +++++--
 .../apache/ranger/common/RangerUserStoreCache.java | 122 ++++++++++++++
 .../org/apache/ranger/db/XXGlobalStateDao.java     |  40 ++---
 .../java/org/apache/ranger/rest/XUserREST.java     | 111 ++++++++++++
 .../apache/ranger/service/XGroupServiceBase.java   |  20 +++
 .../apache/ranger/service/XUserServiceBase.java    |  20 +++
 .../process/LdapDeltaUserGroupBuilder.java         | 127 ++++++++++++--
 .../process/LdapPolicyMgrUserGroupBuilder.java     |  70 ++++----
 .../ldapusersync/process/LdapUserGroupBuilder.java |   4 +-
 .../unixusersync/config/UserGroupSyncConfig.java   |  92 ++++++++++
 .../ranger/unixusersync/model/MUserInfo.java       |  10 +-
 .../ranger/unixusersync/model/XGroupInfo.java      |  11 +-
 .../ranger/unixusersync/model/XUserInfo.java       |  11 +-
 .../process/PolicyMgrUserGroupBuilder.java         |  14 +-
 .../apache/ranger/usergroupsync/UserGroupSink.java |   7 +-
 .../LdapPolicyMgrUserGroupBuilderTest.java         |  10 +-
 .../PolicyMgrUserGroupBuilderTest.java             |   8 +-
 26 files changed, 1172 insertions(+), 104 deletions(-)

diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/GroupInfo.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/GroupInfo.java
new file mode 100644
index 0000000..5925014
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/GroupInfo.java
@@ -0,0 +1,87 @@
+/*
+ * 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.model;
+
+import org.apache.ranger.plugin.util.RangerUserStoreUtil;
+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.util.HashMap;
+import java.util.Map;
+
+@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown=true)
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class GroupInfo extends RangerBaseModelObject implements java.io.Serializable {
+
+    private static final long serialVersionUID = 1L;
+    private String                  name;
+    private String                  description;
+    private Map<String, String>     otherAttributes;
+
+    public GroupInfo() {
+        this(null, null, null);
+    }
+
+    public GroupInfo(String name, String description, Map<String, String> otherAttributes) {
+        setName(name);
+        setDescription(description);
+        setOtherAttributes(otherAttributes);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Map<String, String> getOtherAttributes() {
+        return otherAttributes;
+    }
+
+    public void setOtherAttributes(Map<String, String> otherAttributes) {
+        this.otherAttributes = otherAttributes == null ? new HashMap<>() : otherAttributes;
+    }
+
+    @Override
+    public String toString() {
+        return "{name=" + name
+                + ", description=" + description
+                + ", otherAttributes=" + RangerUserStoreUtil.getPrintableOptions(otherAttributes)
+                + "}";
+    }
+
+}
\ No newline at end of file
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 5552ce9..5b92566 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
@@ -41,9 +41,10 @@ import java.util.Map;
 public class RangerPluginInfo implements Serializable {
 	private static final long serialVersionUID = 1L;
 
-	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 int ENTITY_TYPE_POLICIES 	= 0;
+	public static final int ENTITY_TYPE_TAGS     	= 1;
+	public static final int ENTITY_TYPE_ROLES	 	= 2;
+	public static final int ENTITY_TYPE_USERSTORE	= 3;
 
 	public static final String PLUGIN_INFO_POLICY_DOWNLOAD_TIME      = "policyDownloadTime";
 	public static final String PLUGIN_INFO_POLICY_DOWNLOADED_VERSION = "policyDownloadedVersion";
@@ -59,6 +60,11 @@ public class RangerPluginInfo implements Serializable {
 	public static final String PLUGIN_INFO_ROLE_ACTIVATION_TIME       = "roleActivationTime";
 	public static final String PLUGIN_INFO_ROLE_ACTIVE_VERSION        = "roleActiveVersion";
 
+	public static final String PLUGIN_INFO_USERSTORE_DOWNLOAD_TIME         = "userstoreDownloadTime";
+	public static final String PLUGIN_INFO_USERSTORE_DOWNLOADED_VERSION    = "userstoreDownloadedVersion";
+	public static final String PLUGIN_INFO_USERSTORE_ACTIVATION_TIME       = "userstoreActivationTime";
+	public static final String PLUGIN_INFO_USERSTORE_ACTIVE_VERSION        = "userstoreActiveVersion";
+
 	public static final String RANGER_ADMIN_LAST_POLICY_UPDATE_TIME  = "lastPolicyUpdateTime";
 	public static final String RANGER_ADMIN_LATEST_POLICY_VERSION    = "latestPolicyVersion";
 	public static final String RANGER_ADMIN_LAST_TAG_UPDATE_TIME     = "lastTagUpdateTime";
@@ -344,6 +350,50 @@ public class RangerPluginInfo implements Serializable {
 	}
 
 	@JsonIgnore
+	public void setUserStoreDownloadTime(Long userstoreDownloadTime) {
+		getInfo().put(PLUGIN_INFO_USERSTORE_DOWNLOAD_TIME, userstoreDownloadTime == null ? null : Long.toString(userstoreDownloadTime));
+	}
+
+	@JsonIgnore
+	public Long getUserStoreDownloadTime() {
+		String downloadTimeString = getInfo().get(PLUGIN_INFO_USERSTORE_DOWNLOAD_TIME);
+		return StringUtils.isNotBlank(downloadTimeString) ? Long.valueOf(downloadTimeString) : null;
+	}
+
+	@JsonIgnore
+	public void setUserStoreDownloadedVersion(Long userstoreDownloadedVersion) {
+		getInfo().put(PLUGIN_INFO_USERSTORE_DOWNLOADED_VERSION, userstoreDownloadedVersion == null ? null : Long.toString(userstoreDownloadedVersion));
+	}
+
+	@JsonIgnore
+	public Long getUserStoreDownloadedVersion() {
+		String downloadedVersionString = getInfo().get(PLUGIN_INFO_USERSTORE_DOWNLOADED_VERSION);
+		return StringUtils.isNotBlank(downloadedVersionString) ? Long.valueOf(downloadedVersionString) : null;
+	}
+
+	@JsonIgnore
+	public void setUserStoreActivationTime(Long userstoreActivationTime) {
+		getInfo().put(PLUGIN_INFO_USERSTORE_ACTIVATION_TIME, userstoreActivationTime == null ? null : Long.toString(userstoreActivationTime));
+	}
+
+	@JsonIgnore
+	public Long getUserStoreActivationTime() {
+		String activationTimeString = getInfo().get(PLUGIN_INFO_USERSTORE_ACTIVATION_TIME);
+		return StringUtils.isNotBlank(activationTimeString) ? Long.valueOf(activationTimeString) : null;
+	}
+
+	@JsonIgnore
+	public void setUserStoreActiveVersion(Long userstoreActiveVersion) {
+		getInfo().put(PLUGIN_INFO_USERSTORE_ACTIVE_VERSION, userstoreActiveVersion == null ? null : Long.toString(userstoreActiveVersion));
+	}
+
+	@JsonIgnore
+	public Long getUserStoreActiveVersion() {
+		String activeVersionString = getInfo().get(PLUGIN_INFO_USERSTORE_ACTIVE_VERSION);
+		return StringUtils.isNotBlank(activeVersionString) ? Long.valueOf(activeVersionString) : null;
+	}
+
+	@JsonIgnore
 	public void setPluginCapabilities(String capabilities) {
 		setCapabilities(PLUGIN_INFO_CAPABILITIES, capabilities);
 	}
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/UserInfo.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/UserInfo.java
new file mode 100644
index 0000000..3e933e8
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/UserInfo.java
@@ -0,0 +1,95 @@
+/*
+ * 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.model;
+
+import org.apache.ranger.plugin.util.RangerUserStoreUtil;
+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.util.*;
+
+@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown=true)
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class UserInfo extends RangerBaseModelObject implements java.io.Serializable {
+
+    private static final long serialVersionUID = 1L;
+    private String                  name;
+    private String                  description;
+    private Map<String, String>     otherAttributes;
+    private Set<String>             groups;
+
+    public UserInfo() {
+        this(null, null, null);
+    }
+
+    public UserInfo(String name, String description, Map<String, String> otherAttributes) {
+        setName(name);
+        setDescription(description);
+        setOtherAttributes(otherAttributes);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Map<String, String> getOtherAttributes() {
+        return otherAttributes;
+    }
+
+    public void setOtherAttributes(Map<String, String> otherAttributes) {
+        this.otherAttributes = otherAttributes == null ? new HashMap<>() : otherAttributes;
+    }
+
+    public Set<String> getGroups(){
+        return this.groups;
+    }
+
+    public void setGroups(Set<String> groups){
+        this.groups = groups;
+    }
+
+    @Override
+    public String toString() {
+        return "{name=" + name
+                + ", description=" + description
+                + ", otherAttributes=" + RangerUserStoreUtil.getPrintableOptions(otherAttributes)
+                + ", groups=" + groups
+                + "}";
+    }
+}
\ No newline at end of file
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerUserStore.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerUserStore.java
new file mode 100644
index 0000000..dfe742f
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerUserStore.java
@@ -0,0 +1,186 @@
+/*
+ * 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.collections.MapUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.ranger.plugin.model.GroupInfo;
+import org.apache.ranger.plugin.model.UserInfo;
+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.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+@JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown=true)
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class RangerUserStore implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public static final String CLOUD_IDENTITY_NAME = "cloud_id";
+
+    private Long                             userStoreVersion;
+    private Date                             userStoreUpdateTime;
+    private Map<String, Map<String, String>> userAttrMapping;
+    private Map<String, Map<String, String>> groupAttrMapping ;
+    private Map<String, Set<String>>         userGroupMapping;
+    private Map<String, String>              userCloudIdMapping;
+    private Map<String, String>              groupCloudIdMapping;
+
+    public RangerUserStore() {this(-1L, null, null);}
+
+    public RangerUserStore(Long userStoreVersion, Set<UserInfo> users, Set<GroupInfo> groups ) {
+        setUserStoreVersion(userStoreVersion);
+        setUserStoreUpdateTime(new Date());
+        buildMap(users, groups);
+    }
+    public Long getUserStoreVersion() {
+        return userStoreVersion;
+    }
+
+    public void setUserStoreVersion(Long userStoreVersion) {
+        this.userStoreVersion = userStoreVersion;
+    }
+
+    public Date getUserStoreUpdateTime() {
+        return userStoreUpdateTime;
+    }
+
+    public void setUserStoreUpdateTime(Date userStoreUpdateTime) {
+        this.userStoreUpdateTime = userStoreUpdateTime;
+    }
+
+    public Map<String, Map<String, String>> getUserAttrMapping() {
+        return userAttrMapping;
+    }
+
+    public void setUserAttrMapping(Map<String, Map<String, String>> userAttrMapping) {
+        this.userAttrMapping = userAttrMapping;
+    }
+
+    public Map<String, Map<String, String>> getGroupAttrMapping() {
+        return groupAttrMapping;
+    }
+
+    public void setGroupAttrMapping(Map<String, Map<String, String>> groupAttrMapping) {
+        this.groupAttrMapping = groupAttrMapping;
+    }
+
+    public Map<String, Set<String>> getUserGroupMapping() {
+        return userGroupMapping;
+    }
+
+    public void setUserGroupMapping(Map<String, Set<String>> userGroupMapping) {
+        this.userGroupMapping = userGroupMapping;
+    }
+
+    public Map<String, String> getUserCloudIdMapping() {
+        return userCloudIdMapping;
+    }
+
+    public void setUserCloudIdMapping(Map<String, String> userCloudIdMapping) {
+        this.userCloudIdMapping = userCloudIdMapping;
+    }
+
+    public Map<String, String> getGroupCloudIdMapping() {
+        return groupCloudIdMapping;
+    }
+
+    public void setGroupCloudIdMapping(Map<String, String> groupCloudIdMapping) {
+        this.groupCloudIdMapping = groupCloudIdMapping;
+    }
+
+    @Override
+    public String toString( ) {
+        StringBuilder sb = new StringBuilder();
+
+        toString(sb);
+
+        return sb.toString();
+    }
+
+    public StringBuilder toString(StringBuilder sb) {
+        sb.append("RangerUserStore={")
+                .append("userStoreVersion=").append(userStoreVersion).append(", ")
+                .append("userStoreUpdateTime=").append(userStoreUpdateTime).append(", ");
+        sb.append("users={");
+        if(MapUtils.isNotEmpty(userAttrMapping)) {
+            for(String user : userAttrMapping.keySet()) {
+                sb.append(user);
+            }
+        }
+        sb.append("}, ");
+        sb.append("groups={");
+        if(MapUtils.isNotEmpty(groupAttrMapping)) {
+            for(String group : groupAttrMapping.keySet()) {
+                sb.append(group);
+            }
+        }
+        sb.append("}");
+        sb.append("}");
+
+        return sb;
+    }
+
+    private void buildMap(Set<UserInfo> users, Set<GroupInfo> groups) {
+        if (CollectionUtils.isNotEmpty(users)) {
+            userAttrMapping = new HashMap<>();
+            userCloudIdMapping = new HashMap<>();
+            userGroupMapping = new HashMap<>();
+            for (UserInfo user : users) {
+                String username = user.getName();
+                Map<String, String> userAttrs = user.getOtherAttributes();
+                if (MapUtils.isNotEmpty(userAttrs)) {
+                    userAttrMapping.put(username, userAttrs);
+                    String cloudId = userAttrs.get(CLOUD_IDENTITY_NAME);
+                    if (StringUtils.isNotEmpty(cloudId)) {
+                        userCloudIdMapping.put(cloudId, username);
+                    }
+                }
+                userGroupMapping.put(username, user.getGroups());
+            }
+        }
+        if (CollectionUtils.isNotEmpty(groups)) {
+            groupAttrMapping = new HashMap<>();
+            groupCloudIdMapping = new HashMap<>();
+            for (GroupInfo group : groups) {
+                String groupname = group.getName();
+                Map<String, String> groupAttrs = group.getOtherAttributes();
+                if (MapUtils.isNotEmpty(groupAttrs)) {
+                    groupAttrMapping.put(groupname, groupAttrs);
+                    String cloudId = groupAttrs.get(CLOUD_IDENTITY_NAME);
+                    if (StringUtils.isNotEmpty(cloudId)) {
+                        groupCloudIdMapping.put(cloudId, groupname);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerUserStoreUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerUserStoreUtil.java
new file mode 100644
index 0000000..f66eb1f
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerUserStoreUtil.java
@@ -0,0 +1,60 @@
+/*
+ * 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.MapUtils;
+import org.apache.commons.lang.StringUtils;
+
+import java.util.Map;
+
+public class RangerUserStoreUtil {
+    public static final String CLOUD_IDENTITY_NAME = "cloud_id";
+
+    public static String getPrintableOptions(Map<String, String> otherAttributes) {
+        if (MapUtils.isEmpty(otherAttributes)) return "{}";
+        StringBuilder ret = new StringBuilder();
+        ret.append("{");
+        for (Map.Entry<String, String> entry : otherAttributes.entrySet()) {
+            ret.append(entry.getKey()).append(", ").append("[").append(entry.getValue()).append("]").append(",");
+        }
+        ret.append("}");
+        return ret.toString();
+    }
+
+    public static String getAttrVal(Map<String, Map<String, String>> attrMap, String name, String attrName) {
+        String ret = null;
+
+        if (StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(attrName)) {
+            Map<String, String> attrs = attrMap.get(name);
+            if (MapUtils.isNotEmpty(attrs)) {
+                ret = attrs.get(attrName);
+            }
+        }
+        return ret;
+    }
+
+    public String getCloudId(Map<String, Map<String, String>> attrMap, String name) {
+        String cloudId = null;
+        cloudId = getAttrVal(attrMap, name, CLOUD_IDENTITY_NAME);
+        return cloudId;
+    }
+}
+
+
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 0f44888..17c105f 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
@@ -707,6 +707,12 @@ public class AssetMgr extends AssetMgrBase {
 				pluginSvcVersionInfo.setRoleDownloadedVersion(downloadedVersion);
 				pluginSvcVersionInfo.setRoleDownloadTime(new Date().getTime());
 				break;
+			case RangerPluginInfo.ENTITY_TYPE_USERSTORE:
+				pluginSvcVersionInfo.setUserStoreActiveVersion(lastKnownVersion);
+				pluginSvcVersionInfo.setUserStoreActivationTime(lastActivationTime);
+				pluginSvcVersionInfo.setUserStoreDownloadedVersion(downloadedVersion);
+				pluginSvcVersionInfo.setUserStoreDownloadTime(new Date().getTime());
+				break;
 		}
 
 		createOrUpdatePluginInfo(pluginSvcVersionInfo, entityType , httpCode, clusterName);
@@ -735,6 +741,9 @@ public class AssetMgr extends AssetMgrBase {
 				case RangerPluginInfo.ENTITY_TYPE_ROLES:
 					isTagVersionResetNeeded = false;
 					break;
+				case RangerPluginInfo.ENTITY_TYPE_USERSTORE:
+					isTagVersionResetNeeded = false;
+					break;
 				default:
 					isTagVersionResetNeeded = false;
 					break;
@@ -751,7 +760,8 @@ public class AssetMgr extends AssetMgrBase {
 			Runnable commitWork;
 			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))) {
+					|| (isRoleDownloadRequest(entityType) && (pluginInfo.getRoleActiveVersion() == null || pluginInfo.getRoleActiveVersion() == -1))
+					|| (isUserStoreDownloadRequest(entityType) && (pluginInfo.getUserStoreActiveVersion() == null || pluginInfo.getUserStoreActiveVersion() == -1))) {
 				commitWork = new Runnable() {
 					@Override
 					public void run() {
@@ -804,11 +814,16 @@ public class AssetMgr extends AssetMgrBase {
 						// This is our best guess of when tags may have been downloaded
 						pluginInfo.setTagDownloadTime(pluginInfo.getTagActivationTime());
 					}
-				} else {
+				} else if (isRoleDownloadRequest(entityType)) {
 					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());
 					}
+				} else {
+					if (pluginInfo.getUserStoreDownloadTime() != null && pluginInfo.getUserStoreDownloadedVersion().equals(pluginInfo.getUserStoreActiveVersion())) {
+						// This is our best guess of when users and groups may have been downloaded
+						pluginInfo.setUserStoreDownloadTime(pluginInfo.getUserStoreActivationTime());
+					}
 				}
 
 				pluginInfo.setAdminCapabilities(adminCapabilities);
@@ -891,7 +906,7 @@ public class AssetMgr extends AssetMgrBase {
 						dbObj.setTagActivationTime(lastTagActivationTime);
 						needsUpdating = true;
 					}
-				} else {
+				} else if (isRoleDownloadRequest(entityType)){
 					if (dbObj.getRoleDownloadedVersion() == null || !dbObj.getRoleDownloadedVersion().equals(pluginInfo.getRoleDownloadedVersion())) {
 						dbObj.setRoleDownloadedVersion(pluginInfo.getRoleDownloadedVersion());
 						dbObj.setRoleDownloadTime(pluginInfo.getRoleDownloadTime());
@@ -915,6 +930,30 @@ public class AssetMgr extends AssetMgrBase {
 						dbObj.setRoleActivationTime(lastRoleActivationTime);
 						needsUpdating = true;
 					}
+				} else {
+					if (dbObj.getUserStoreDownloadedVersion() == null || !dbObj.getUserStoreDownloadedVersion().equals(pluginInfo.getUserStoreDownloadedVersion())) {
+						dbObj.setUserStoreDownloadedVersion(pluginInfo.getUserStoreDownloadedVersion());
+						dbObj.setUserStoreDownloadTime(pluginInfo.getUserStoreDownloadTime());
+						needsUpdating = true;
+					}
+
+					Long lastKnownUserStoreVersion = pluginInfo.getUserStoreActiveVersion();
+					Long lastUserStoreActivationTime = pluginInfo.getUserStoreActivationTime();
+
+					if (lastKnownUserStoreVersion != null && lastKnownUserStoreVersion == -1) {
+						dbObj.setUserStoreDownloadTime(pluginInfo.getUserStoreDownloadTime());
+						needsUpdating = true;
+					}
+
+					if (lastKnownUserStoreVersion != null && lastKnownUserStoreVersion > 0 && (dbObj.getUserStoreActiveVersion() == null || !dbObj.getUserStoreActiveVersion().equals(lastKnownUserStoreVersion))) {
+						dbObj.setUserStoreActiveVersion(lastKnownUserStoreVersion);
+						needsUpdating = true;
+					}
+
+					if (lastUserStoreActivationTime != null && lastUserStoreActivationTime > 0 && (dbObj.getUserStoreActivationTime() == null || !dbObj.getUserStoreActivationTime().equals(lastUserStoreActivationTime))) {
+						dbObj.setUserStoreActivationTime(lastUserStoreActivationTime);
+						needsUpdating = true;
+					}
 				}
 
 				if (isTagVersionResetNeeded) {
@@ -1256,4 +1295,8 @@ public class AssetMgr extends AssetMgrBase {
 	private boolean isRoleDownloadRequest(int entityType) {
 		return entityType == RangerPluginInfo.ENTITY_TYPE_ROLES;
 	}
+
+	private boolean isUserStoreDownloadRequest(int entityType) {
+		return entityType == RangerPluginInfo.ENTITY_TYPE_USERSTORE;
+	}
 }
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 04596dc..5be8d9d 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
@@ -293,7 +293,7 @@ public class RoleDBStore implements RoleStore {
             XXServiceVersionInfo xxServiceVersionInfo = daoMgr.getXXServiceVersionInfo().findByServiceName(serviceName);
             ret = (xxServiceVersionInfo != null) ? xxServiceVersionInfo.getRoleVersion() : null;
         } else {
-            ret = daoMgr.getXXGlobalState().getRoleVersion(RANGER_ROLE_GLOBAL_STATE_NAME);
+            ret = daoMgr.getXXGlobalState().getAppDataVersion(RANGER_ROLE_GLOBAL_STATE_NAME);
         }
 
         return ret;
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 ccda6ab..47250f3 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
@@ -3487,7 +3487,7 @@ public class ServiceDBStore extends AbstractServiceStore {
 				// get the LatestRoleVersion from the GlobalTable and update ServiceInfo for a service
 				XXGlobalStateDao xxGlobalStateDao = daoMgr.getXXGlobalState();
 				if (xxGlobalStateDao != null) {
-					Long roleVersion = xxGlobalStateDao.getRoleVersion("RangerRole");
+					Long roleVersion = xxGlobalStateDao.getAppDataVersion("RangerRole");
 					if (roleVersion != null) {
 						serviceVersionInfoDbObj.setRoleVersion(roleVersion);
 						serviceVersionInfoDbObj.setRoleUpdateTime(now);
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/UserMgr.java b/security-admin/src/main/java/org/apache/ranger/biz/UserMgr.java
index 3045eaf..2b3cdcb 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/UserMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/UserMgr.java
@@ -572,6 +572,7 @@ public class UserMgr {
 		gjUser.setPassword(userProfile.getPassword());
 		gjUser.setUserSource(userProfile.getUserSource());
 		gjUser.setPublicScreenName(userProfile.getPublicScreenName());
+		gjUser.setOtherAttributes(userProfile.getOtherAttributes());
 		if (userProfile.getFirstName() != null
 				&& userProfile.getLastName() != null
 				&& !userProfile.getFirstName().trim().isEmpty()
@@ -1171,6 +1172,7 @@ public class UserMgr {
 				 */
 			}
         }
+
         VXPortalUser userProfileRes = null;
         if (xXPortalUser != null) {
             userProfileRes = mapXXPortalUserToVXPortalUserForDefaultAccount(xXPortalUser);
@@ -1246,6 +1248,7 @@ public class UserMgr {
 		userProfile.setFirstName(user.getFirstName());
 		userProfile.setLastName(user.getLastName());
 		userProfile.setPublicScreenName(user.getPublicScreenName());
+		userProfile.setOtherAttributes(user.getOtherAttributes());
 
 		List<XXPortalUserRole> gjUserRoleList = daoManager
 				.getXXPortalUserRole().findByParentId(user.getId());
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java b/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
index 1c29d82..bfce9a6 100755
--- a/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
@@ -31,28 +31,21 @@ import java.util.Set;
 
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
-import org.apache.ranger.common.ContextUtil;
-import org.apache.ranger.common.GUIDUtil;
-import org.apache.ranger.common.RangerCommonEnums;
+import org.apache.ranger.common.*;
 import org.apache.ranger.entity.XXGroupPermission;
 import org.apache.ranger.entity.XXModuleDef;
 import org.apache.ranger.entity.XXUserPermission;
+import org.apache.ranger.plugin.model.GroupInfo;
 import org.apache.ranger.plugin.model.RangerPolicy;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerDataMaskPolicyItem;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerRowFilterPolicyItem;
+import org.apache.ranger.plugin.model.UserInfo;
+import org.apache.ranger.plugin.util.RangerUserStore;
 import org.apache.ranger.security.context.RangerAPIMapping;
 import org.apache.ranger.service.*;
 import org.apache.ranger.view.*;
 import org.apache.log4j.Logger;
-import org.apache.ranger.common.AppConstants;
-import org.apache.ranger.common.MessageEnums;
-import org.apache.ranger.common.PropertiesUtil;
-import org.apache.ranger.common.RangerConstants;
-import org.apache.ranger.common.RangerServicePoliciesCache;
-import org.apache.ranger.common.SearchCriteria;
-import org.apache.ranger.common.StringUtil;
-import org.apache.ranger.common.UserSessionBase;
 import org.apache.ranger.db.RangerDaoManager;
 import org.apache.ranger.db.XXAuditMapDao;
 import org.apache.ranger.db.XXAuthSessionDao;
@@ -93,6 +86,8 @@ import org.apache.ranger.entity.XXPortalUserRole;
 @Component
 public class XUserMgr extends XUserMgrBase {
 
+	private static final String RANGER_USER_GROUP_GLOBAL_STATE_NAME = "RangerUserStore";
+
 	@Autowired
 	XUserService xUserService;
 
@@ -584,7 +579,11 @@ public class XUserMgr extends XUserMgrBase {
             assignPermissionToUser(vXPortalUser, true);
         }
 		vxUGInfo.setXgroupInfo(vxg);
-
+        try {
+			daoManager.getXXGlobalState().onGlobalAppDataChange(RANGER_USER_GROUP_GLOBAL_STATE_NAME);
+		} catch (Exception excp) {
+			logger.error("createXUserGroupFromMap(" + vXUser.getName() + ") failed", excp);
+		}
 		return vxUGInfo;
 	}
 	
@@ -642,6 +641,12 @@ public class XUserMgr extends XUserMgrBase {
 
 		vxGUInfo.setXuserInfo(vxu);
 
+		try {
+			daoManager.getXXGlobalState().onGlobalAppDataChange(RANGER_USER_GROUP_GLOBAL_STATE_NAME);
+		} catch (Exception excp) {
+			logger.error("createXGroupUserFromMap(" + vXGroup.getName() + ") failed", excp);
+		}
+
 		return vxGUInfo;
 	}
 	
@@ -2544,4 +2549,34 @@ public class XUserMgr extends XUserMgrBase {
 		return vxUgsyncAuditInfo;
 	}
 
+	public Long getUserStoreVersion() {
+		return daoManager.getXXGlobalState().getAppDataVersion(RANGER_USER_GROUP_GLOBAL_STATE_NAME);
+	}
+
+	public Set<UserInfo> getUsers() {
+		return new HashSet<>(xUserService.getUsers());
+	}
+
+	public Set<GroupInfo> getGroups() {
+		return  new HashSet<>(xGroupService.getGroups());
+	}
+
+	public RangerUserStore getRangerUserStore(Long lastKnownUserStoreVersion) throws Exception {
+		RangerUserStore ret                   = null;
+		Long        rangerUserStoreVersionInDB = getUserStoreVersion();
+
+		if (logger.isDebugEnabled()) {
+			logger.debug("==> XUserMgr.getRangerUserStore() lastKnownUserStoreVersion= " + lastKnownUserStoreVersion + " rangerUserStoreVersionInDB= " + rangerUserStoreVersionInDB);
+		}
+
+		if (rangerUserStoreVersionInDB != null) {
+			ret = RangerUserStoreCache.getInstance().getLatestRangerUserStoreOrCached(this, lastKnownUserStoreVersion, rangerUserStoreVersionInDB);
+		}
+
+		if (logger.isDebugEnabled()) {
+			logger.debug("<= XUserMgr.getRangerUserStore() lastKnownUserStoreVersion= " + lastKnownUserStoreVersion + " rangerUserStoreVersionInDB= " + rangerUserStoreVersionInDB + " RangerRoles= " + ret);
+		}
+
+		return ret;
+	}
 }
diff --git a/security-admin/src/main/java/org/apache/ranger/common/RangerUserStoreCache.java b/security-admin/src/main/java/org/apache/ranger/common/RangerUserStoreCache.java
new file mode 100644
index 0000000..8ffc98c
--- /dev/null
+++ b/security-admin/src/main/java/org/apache/ranger/common/RangerUserStoreCache.java
@@ -0,0 +1,122 @@
+/*
+ * 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.collections.CollectionUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig;
+import org.apache.ranger.biz.XUserMgr;
+import org.apache.ranger.plugin.model.GroupInfo;
+import org.apache.ranger.plugin.model.UserInfo;
+import org.apache.ranger.plugin.util.RangerUserStore;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class RangerUserStoreCache {
+	private static final Log LOG = LogFactory.getLog(RangerUserStoreCache.class);
+
+	private static final int MAX_WAIT_TIME_FOR_UPDATE = 10;
+
+	public static volatile RangerUserStoreCache 	sInstance = null;
+	private final int 								waitTimeInSeconds;
+	private final ReentrantLock 					lock = new ReentrantLock();
+	private RangerUserStore 						rangerUserStore;
+
+	public static RangerUserStoreCache getInstance() {
+		if (sInstance == null) {
+			synchronized (RangerUserStoreCache.class) {
+				if (sInstance == null) {
+					sInstance = new RangerUserStoreCache();
+				}
+			}
+		}
+		return sInstance;
+	}
+
+	private RangerUserStoreCache() {
+		RangerAdminConfig config = RangerAdminConfig.getInstance();
+		waitTimeInSeconds = config.getInt("ranger.admin.userstore.download.cache.max.waittime.for.update", MAX_WAIT_TIME_FOR_UPDATE);
+		this.rangerUserStore = new RangerUserStore();
+	}
+
+	public RangerUserStore getRangerUserStore() {
+		return this.rangerUserStore;
+	}
+
+	public RangerUserStore getLatestRangerUserStoreOrCached(XUserMgr xUserMgr, Long lastKnownUserStoreVersion, Long rangerUserStoreVersionInDB) throws Exception {
+		RangerUserStore ret = null;
+
+		if (lastKnownUserStoreVersion == null || !lastKnownUserStoreVersion.equals(rangerUserStoreVersionInDB)) {
+			ret = getLatestRangerUserStore(xUserMgr, lastKnownUserStoreVersion, rangerUserStoreVersionInDB);
+		} else if (lastKnownUserStoreVersion.equals(rangerUserStoreVersionInDB)) {
+			ret = null;
+		} else {
+			ret = getRangerUserStore();
+		}
+
+		return ret;
+	}
+
+	public RangerUserStore getLatestRangerUserStore(XUserMgr xUserMgr, Long lastKnownUserStoreVersion, Long rangerUserStoreVersionInDB) throws Exception {
+		RangerUserStore ret	 = null;
+		boolean         lockResult   = false;
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerUserStoreCache.getLatestRangerUserStore(lastKnownUserStoreVersion= " + lastKnownUserStoreVersion + " rangerUserStoreVersionInDB= " + rangerUserStoreVersionInDB + ")");
+		}
+
+		try {
+			lockResult = lock.tryLock(waitTimeInSeconds, TimeUnit.SECONDS);
+
+			if (lockResult) {
+				final Set<UserInfo> rangerUsersInDB = xUserMgr.getUsers();
+				final Set<GroupInfo> rangerGroupsInDB = xUserMgr.getGroups();
+				if (CollectionUtils.isNotEmpty(rangerUsersInDB)) {
+					for (UserInfo userInfo : rangerUsersInDB) {
+						//Get user group mapping from DB and update userInfo object.
+						userInfo.setGroups(xUserMgr.getGroupsForUser(userInfo.getName()));
+					}
+				}
+
+				ret = new RangerUserStore(rangerUserStoreVersionInDB, rangerUsersInDB, rangerGroupsInDB);
+				rangerUserStore = ret;
+			} else {
+				if (LOG.isDebugEnabled()) {
+					LOG.debug("Could not get lock in [" + waitTimeInSeconds + "] seconds, returning cached RangerUserStore");
+				}
+				ret = getRangerUserStore();
+			}
+		} catch (InterruptedException exception) {
+			LOG.error("RangerUserStoreCache.getLatestRangerUserStore:lock got interrupted..", exception);
+		} finally {
+			if (lockResult) {
+				lock.unlock();
+			}
+		}
+
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerUserStoreCache.getLatestRangerUserStore(lastKnownUserStoreVersion= " + lastKnownUserStoreVersion + " rangerUserStoreVersionInDB= " + rangerUserStoreVersionInDB + " RangerUserStore= " + 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 2e462bd..b979459 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
@@ -35,7 +35,7 @@ import java.util.Map;
 public class XXGlobalStateDao extends BaseDao<XXGlobalState> {
     private static final Logger logger = Logger.getLogger(XXGlobalStateDao.class);
 
-    final static String APP_DATA_ENTRY_ROLE_VERSION = "RangerRoleVersion";
+    final static String APP_DATA_ENTRY_VERSION = "Version";
 
     public void onGlobalStateChange(String stateName) throws Exception {
 
@@ -74,9 +74,9 @@ public class XXGlobalStateDao extends BaseDao<XXGlobalState> {
             try {
                 XXGlobalState globalState = findByStateName(stateName);
                 if (globalState == null) {
-                    createGlobalStateForRoleVersion(stateName);
+                    createGlobalStateForAppDataVersion(stateName);
                 } else {
-                    updateGlobalStateForRoleVersion(globalState, stateName);
+                    updateGlobalStateForAppDataVersion(globalState, stateName);
                 }
             } catch (Exception exception) {
                 logger.error("Cannot create/update GlobalState for state:[" + stateName + "]", exception);
@@ -85,21 +85,21 @@ public class XXGlobalStateDao extends BaseDao<XXGlobalState> {
         }
     }
 
-    public Long getRoleVersion(String stateName) {
+    public Long getAppDataVersion(String stateName) {
         Long ret = null;
         try {
             XXGlobalState       globalState     = findByStateName(stateName);
             if (globalState != null) {
-                Map<String, String> roleVersionJson = new Gson().fromJson(globalState.getAppData(), Map.class);
-                if (MapUtils.isNotEmpty(roleVersionJson)) {
-                    ret = Long.valueOf(roleVersionJson.get(APP_DATA_ENTRY_ROLE_VERSION));
+                Map<String, String> appDataVersionJson = new Gson().fromJson(globalState.getAppData(), Map.class);
+                if (MapUtils.isNotEmpty(appDataVersionJson)) {
+                    ret = Long.valueOf(appDataVersionJson.get(APP_DATA_ENTRY_VERSION));
                 } else {
                     ret = 1L;
                 }
             }
         } catch (Exception exception) {
             if (logger.isDebugEnabled()) {
-                logger.debug("Unable to find the role version in Ranger Database", exception);
+                logger.debug("Unable to find the version for " + stateName + " in Ranger Database", exception);
             }
         }
         return ret;
@@ -140,25 +140,25 @@ public class XXGlobalStateDao extends BaseDao<XXGlobalState> {
         }
     }
 
-    private void createGlobalStateForRoleVersion(String stateName) {
+    private void createGlobalStateForAppDataVersion(String stateName) {
         XXGlobalState globalState = new XXGlobalState();
         globalState.setStateName(stateName);
-        Map<String,String> roleVersion = new HashMap<>();
-        roleVersion.put(APP_DATA_ENTRY_ROLE_VERSION,new String(Long.toString(1L)));
-        globalState.setAppData(new Gson().toJson(roleVersion));
+        Map<String,String> appDataVersion = new HashMap<>();
+        appDataVersion.put(APP_DATA_ENTRY_VERSION,new String(Long.toString(1L)));
+        globalState.setAppData(new Gson().toJson(appDataVersion));
         create(globalState);
     }
 
-    private void updateGlobalStateForRoleVersion(XXGlobalState globalState, String stateName) {
-        Map<String,String> roleVersionJson = new Gson().fromJson(globalState.getAppData(),Map.class);
-        if (MapUtils.isNotEmpty(roleVersionJson)) {
-            Long roleVersion = Long.valueOf(roleVersionJson.get(APP_DATA_ENTRY_ROLE_VERSION)) + 1L;
-            roleVersionJson.put(APP_DATA_ENTRY_ROLE_VERSION, new String(Long.toString(roleVersion)));
-            globalState.setAppData(new Gson().toJson(roleVersionJson));
+    private void updateGlobalStateForAppDataVersion(XXGlobalState globalState, String stateName) {
+        Map<String,String> appDataVersionJson = new Gson().fromJson(globalState.getAppData(),Map.class);
+        if (MapUtils.isNotEmpty(appDataVersionJson)) {
+            Long appDataVersion = Long.valueOf(appDataVersionJson.get(APP_DATA_ENTRY_VERSION)) + 1L;
+            appDataVersionJson.put(APP_DATA_ENTRY_VERSION, new String(Long.toString(appDataVersion)));
+            globalState.setAppData(new Gson().toJson(appDataVersionJson));
             update(globalState);
         } else {
-            //if not present create Global State for Role Version.
-            createGlobalStateForRoleVersion(stateName);
+            //if not present create Global State for state name Version.
+            createGlobalStateForAppDataVersion(stateName);
         }
     }
 }
diff --git a/security-admin/src/main/java/org/apache/ranger/rest/XUserREST.java b/security-admin/src/main/java/org/apache/ranger/rest/XUserREST.java
index 1e8a093..af80639 100644
--- a/security-admin/src/main/java/org/apache/ranger/rest/XUserREST.java
+++ b/security-admin/src/main/java/org/apache/ranger/rest/XUserREST.java
@@ -24,6 +24,7 @@ import java.util.HashMap;
 import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
@@ -32,6 +33,8 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.Context;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.DefaultValue;
 
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
@@ -39,6 +42,9 @@ import org.apache.log4j.Logger;
 import org.apache.ranger.biz.RangerBizUtil;
 import org.apache.ranger.biz.SessionMgr;
 import org.apache.ranger.biz.XUserMgr;
+import org.apache.ranger.biz.AssetMgr;
+import org.apache.ranger.biz.ServiceDBStore;
+import org.apache.ranger.common.ServiceUtil;
 import org.apache.ranger.common.ContextUtil;
 import org.apache.ranger.common.MessageEnums;
 import org.apache.ranger.common.RESTErrorUtil;
@@ -50,6 +56,13 @@ import org.apache.ranger.common.UserSessionBase;
 import org.apache.ranger.common.annotation.RangerAnnotationClassName;
 import org.apache.ranger.common.annotation.RangerAnnotationJSMgrName;
 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.RangerService;
+import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil;
+import org.apache.ranger.plugin.util.RangerRESTUtils;
+import org.apache.ranger.plugin.util.RangerUserStore;
 import org.apache.ranger.security.context.RangerAPIList;
 import org.apache.ranger.service.AuthSessionService;
 import org.apache.ranger.service.XAuditMapService;
@@ -78,6 +91,8 @@ import org.springframework.transaction.annotation.Transactional;
 @Transactional(propagation = Propagation.REQUIRES_NEW)
 public class XUserREST {
 
+	public static final String USERSTORE_DOWNLOAD_USERS = "userstore.download.auth.users";
+
 	@Autowired
 	SearchUtil searchUtil;
 
@@ -131,6 +146,15 @@ public class XUserREST {
 	
 	@Autowired
 	StringUtil stringUtil;
+
+	@Autowired
+	AssetMgr assetMgr;
+
+	@Autowired
+	ServiceUtil serviceUtil;
+
+	@Autowired
+	ServiceDBStore svcStore;
 	
 	static final Logger logger = Logger.getLogger(XUserMgr.class);
 
@@ -1235,6 +1259,93 @@ public class XUserREST {
                 }
         }
 
+	@GET
+	@Path("/secure/download/{serviceName}")
+	@Produces({ "application/xml", "application/json" })
+	public RangerUserStore getSecureRangerUserStoreIfUpdated(@PathParam("serviceName") String serviceName,
+															 @QueryParam("lastKnownUserStoreVersion") Long lastKnownUserStoreVersion,
+															 @DefaultValue("0") @QueryParam("lastActivationTime") Long lastActivationTime,
+															 @QueryParam("pluginId") String pluginId,
+															 @DefaultValue("") @QueryParam("clusterName") String clusterName,
+															 @DefaultValue("") @QueryParam(RangerRESTUtils.REST_PARAM_CAPABILITIES) String pluginCapabilities,
+															 @Context HttpServletRequest request) throws Exception {
+		if (logger.isDebugEnabled()) {
+			logger.debug("==> XUserREST.getSecureRangerUserStoreIfUpdated("
+					+ serviceName + ", " + lastKnownUserStoreVersion + ", " + lastActivationTime + ")");
+		}
+		RangerUserStore ret = null;
+		int     httpCode          = HttpServletResponse.SC_OK;
+		String  logMsg            = null;
+		boolean isAllowed         = false;
+		boolean isAdmin           = bizUtil.isAdmin();
+		boolean isKeyAdmin        = bizUtil.isKeyAdmin();
+		Long    downloadedVersion = null;
+
+		boolean isValid = false;
+		try {
+			XXService xService = rangerDaoManager.getXXService().findByName(serviceName);
+			if (xService != null) {
+				isValid = true;
+			}
+			if (isValid) {
+				if (lastKnownUserStoreVersion == null) {
+					lastKnownUserStoreVersion = Long.valueOf(-1);
+				}
+				XXServiceDef xServiceDef = rangerDaoManager.getXXServiceDef().getById(xService.getType());
+				RangerService rangerService = svcStore.getServiceByName(serviceName);
+
+				if (StringUtils.equals(xServiceDef.getImplclassname(), EmbeddedServiceDefsUtil.KMS_IMPL_CLASS_NAME)) {
+					if (isKeyAdmin) {
+						isAllowed = true;
+					} else {
+						isAllowed = bizUtil.isUserAllowed(rangerService, USERSTORE_DOWNLOAD_USERS);
+					}
+				} else {
+					if (isAdmin) {
+						isAllowed = true;
+					} else {
+						isAllowed = bizUtil.isUserAllowed(rangerService, USERSTORE_DOWNLOAD_USERS);
+					}
+				}
+
+				if (isAllowed) {
+					RangerUserStore rangerUserStore = xUserMgr.getRangerUserStore(lastKnownUserStoreVersion);
+					if (rangerUserStore == null) {
+						downloadedVersion = lastKnownUserStoreVersion;
+						httpCode = HttpServletResponse.SC_NOT_MODIFIED;
+						logMsg = "No change since last update";
+					} else {
+						downloadedVersion = rangerUserStore.getUserStoreVersion();
+						ret = rangerUserStore;
+						httpCode = HttpServletResponse.SC_OK;
+						logMsg = "Returning RangerUserStore =>" + (ret.toString());
+					}
+				} else {
+					logger.error("getSecureRangerUserStoreIfUpdated(" + serviceName + ", " + lastKnownUserStoreVersion + ") failed as User doesn't have permission to download UsersAndGroups");
+					httpCode = HttpServletResponse.SC_UNAUTHORIZED;
+					logMsg = "User doesn't have permission to download UsersAndGroups";
+				}
+			}
+
+		} catch (Throwable excp) {
+			logger.error("getSecureRangerUserStoreIfUpdated(" + serviceName + ", " + lastKnownUserStoreVersion + ", " + lastActivationTime + ") failed", excp);
+			httpCode = HttpServletResponse.SC_BAD_REQUEST;
+			logMsg = excp.getMessage();
+		}
+
+		assetMgr.createPluginInfo(serviceName, pluginId, request, RangerPluginInfo.ENTITY_TYPE_USERSTORE, downloadedVersion, lastKnownUserStoreVersion, lastActivationTime, httpCode, clusterName, pluginCapabilities);
+
+		if (httpCode != HttpServletResponse.SC_OK) {
+			boolean logError = httpCode != HttpServletResponse.SC_NOT_MODIFIED;
+			throw restErrorUtil.createRESTException(httpCode, logMsg, logError);
+		}
+
+		if (logger.isDebugEnabled()) {
+			logger.debug("<== XUserREST.getSecureRangerUserStoreIfUpdated(" + serviceName + ", " + lastKnownUserStoreVersion + ", " + lastActivationTime + ")" + ret);
+		}
+		return ret;
+	}
+
 	@POST
 	@Path("/ugsync/auditinfo")
 	@Produces({ "application/xml", "application/json" })
diff --git a/security-admin/src/main/java/org/apache/ranger/service/XGroupServiceBase.java b/security-admin/src/main/java/org/apache/ranger/service/XGroupServiceBase.java
index 1a701bb..cde91dc 100644
--- a/security-admin/src/main/java/org/apache/ranger/service/XGroupServiceBase.java
+++ b/security-admin/src/main/java/org/apache/ranger/service/XGroupServiceBase.java
@@ -25,15 +25,20 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import com.google.gson.Gson;
 
+import com.google.gson.GsonBuilder;
 import org.apache.ranger.common.SearchCriteria;
 import org.apache.ranger.entity.XXGroup;
+import org.apache.ranger.plugin.model.GroupInfo;
 import org.apache.ranger.view.VXGroup;
 import org.apache.ranger.view.VXGroupList;
 
 public abstract class XGroupServiceBase<T extends XXGroup, V extends VXGroup>
 		extends AbstractBaseResourceService<T, V> {
 	public static final String NAME = "XGroup";
+	private static final Gson gsonBuilder = new GsonBuilder().create();
 
 	public XGroupServiceBase() {
 
@@ -84,4 +89,19 @@ public abstract class XGroupServiceBase<T extends XXGroup, V extends VXGroup>
 		return returnList;
 	}
 
+	public List<GroupInfo> getGroups() {
+		List<GroupInfo> returnList = new ArrayList<>();
+
+		@SuppressWarnings("unchecked")
+		List<XXGroup> resultList = daoManager.getXXGroup().getAll();
+
+		// Iterate over the result list and create the return list
+		for (XXGroup gjXGroup : resultList) {
+			GroupInfo groupInfo = new GroupInfo(gjXGroup.getName(), gjXGroup.getDescription(), gsonBuilder.fromJson(gjXGroup.getOtherAttributes(), Map.class));
+			returnList.add(groupInfo);
+		}
+
+		return returnList;
+	}
+
 }
diff --git a/security-admin/src/main/java/org/apache/ranger/service/XUserServiceBase.java b/security-admin/src/main/java/org/apache/ranger/service/XUserServiceBase.java
index 1004952..9cdc14e 100644
--- a/security-admin/src/main/java/org/apache/ranger/service/XUserServiceBase.java
+++ b/security-admin/src/main/java/org/apache/ranger/service/XUserServiceBase.java
@@ -25,15 +25,20 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import com.google.gson.Gson;
 
+import com.google.gson.GsonBuilder;
 import org.apache.ranger.common.SearchCriteria;
 import org.apache.ranger.entity.XXUser;
+import org.apache.ranger.plugin.model.UserInfo;
 import org.apache.ranger.view.VXUser;
 import org.apache.ranger.view.VXUserList;
 
 public abstract class XUserServiceBase<T extends XXUser, V extends VXUser>
 		extends AbstractBaseResourceService<T, V> {
 	public static final String NAME = "XUser";
+	private static final Gson gsonBuilder = new GsonBuilder().create();
 
 	public XUserServiceBase() {
 
@@ -84,4 +89,19 @@ public abstract class XUserServiceBase<T extends XXUser, V extends VXUser>
 		return returnList;
 	}
 
+	public List<UserInfo> getUsers() {
+		List<UserInfo> returnList = new ArrayList<>();
+
+		@SuppressWarnings("unchecked")
+		List<XXUser> resultList = daoManager.getXXUser().getAll();
+
+		// Iterate over the result list and create the return list
+		for (XXUser gjXUser : resultList) {
+			UserInfo userInfo = new UserInfo(gjXUser.getName(), gjXUser.getDescription(), gsonBuilder.fromJson(gjXUser.getOtherAttributes(), Map.class));
+			returnList.add(userInfo);
+		}
+
+		return returnList;
+	}
+
 }
diff --git a/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapDeltaUserGroupBuilder.java b/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapDeltaUserGroupBuilder.java
index ca50f09..bea91c4 100644
--- a/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapDeltaUserGroupBuilder.java
+++ b/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapDeltaUserGroupBuilder.java
@@ -33,6 +33,7 @@ import java.util.Properties;
 import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.HashMap;
+import java.util.UUID;
 
 import javax.naming.Context;
 import javax.naming.InvalidNameException;
@@ -51,6 +52,7 @@ import javax.naming.ldap.StartTlsResponse;
 
 import org.apache.commons.collections.BidiMap;
 import org.apache.commons.collections.bidimap.DualHashBidiMap;
+import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
 import org.apache.ranger.unixusersync.config.UserGroupSyncConfig;
 import org.apache.ranger.unixusersync.model.LdapSyncSourceInfo;
@@ -65,6 +67,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 	
 	private static final Logger LOG = Logger.getLogger(LdapDeltaUserGroupBuilder.class);
 	
+	private static final String DATA_TYPE_BYTEARRAY = "byte[]";
 	private static final int PAGE_SIZE = 500;
 	private static long deltaSyncUserTime = 0; // Used for AD uSNChanged 
 	private static long deltaSyncGroupTime = 0; // Used for AD uSNChanged
@@ -80,12 +83,14 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 
   private String[] userSearchBase;
 	private String userNameAttribute;
+	private String userCloudIdAttribute;
   private int    userSearchScope;
   private String userObjectClass;
   private String userSearchFilter;
   private String extendedUserSearchFilter;
   private SearchControls userSearchControls;
   private Set<String> userGroupNameAttributeSet;
+  private Set<String> otherUserAttributes;
 
   private boolean pagedResultsEnabled = true;
   private int pagedResultsSize = PAGE_SIZE;
@@ -102,6 +107,8 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
   private SearchControls groupSearchControls;
   private String groupMemberAttributeName;
   private String groupNameAttribute;
+	private String groupCloudIdAttribute;
+	private Set<String> otherGroupAttributes;
 	private int groupHierarchyLevels;
 
 	private LdapContext ldapContext;
@@ -126,6 +133,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 	int noOfNewGroups;
 	int noOfModifiedUsers;
 	int noOfModifiedGroups;
+	private Map<String, Map<String, String>> groupInfoMap;
 
 	public static void main(String[] args) throws Throwable {
 		LdapDeltaUserGroupBuilder  ugBuilder = new LdapDeltaUserGroupBuilder();
@@ -188,6 +196,32 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 			env.put("java.naming.ldap.factory.socket", "org.apache.ranger.ldapusersync.process.CustomSSLSocketFactory");
 		}
 
+		if (StringUtils.isNotEmpty(userCloudIdAttribute)) {
+			if (config.getUserCloudIdAttributeDataType().equals(DATA_TYPE_BYTEARRAY)) {
+				env.put("java.naming.ldap.attributes.binary", userCloudIdAttribute);
+			}
+		}
+
+		if (StringUtils.isNotEmpty(groupCloudIdAttribute)) {
+			if (config.getGroupCloudIdAttributeDataType().equals(DATA_TYPE_BYTEARRAY)) {
+				env.put("java.naming.ldap.attributes.binary", groupCloudIdAttribute);
+			}
+		}
+
+		for (String otherUserAttribute : otherUserAttributes) {
+			String attrType = config.getOtherUserAttributeDataType(otherUserAttribute);
+			if (attrType.equals(DATA_TYPE_BYTEARRAY)) {
+				env.put("java.naming.ldap.attributes.binary", otherUserAttribute);
+			}
+		}
+
+		for (String otherGroupAttribute : otherGroupAttributes) {
+			String attrType = config.getOtherGroupAttributeDataType(otherGroupAttribute);
+			if (attrType.equals(DATA_TYPE_BYTEARRAY)) {
+				env.put("java.naming.ldap.attributes.binary", otherGroupAttribute);
+			}
+		}
+
 		ldapContext = new InitialLdapContext(env, null);
 		if (!ldapUrl.startsWith("ldaps")) {
 			if (config.isStartTlsEnabled()) {
@@ -227,9 +261,11 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 		userSearchFilter = config.getUserSearchFilter();
 
 		userNameAttribute = config.getUserNameAttribute();
+		userCloudIdAttribute = config.getUserCloudIdAttribute();
 
 		Set<String> userSearchAttributes = new HashSet<String>();
 		userSearchAttributes.add(userNameAttribute);
+		userSearchAttributes.add(userCloudIdAttribute);
 		// For Group based search, user's group name attribute should not be added to the user search attributes
 		if (!groupSearchFirstEnabled && !groupSearchEnabled) {
 			userGroupNameAttributeSet = config.getUserGroupNameAttributeSet();
@@ -237,6 +273,10 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 				userSearchAttributes.add(useGroupNameAttribute);
 			}
 		}
+		otherUserAttributes = config.getOtherUserAttributes();
+		for (String otherUserAttribute : otherUserAttributes) {
+			userSearchAttributes.add(otherUserAttribute);
+		}
 		userSearchAttributes.add("uSNChanged");
 		userSearchAttributes.add("modifytimestamp");
 		userSearchControls = new SearchControls();
@@ -253,6 +293,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
     groupSearchFilter = config.getGroupSearchFilter();
     groupMemberAttributeName =  config.getUserGroupMemberAttributeName();
     groupNameAttribute = config.getGroupNameAttribute();
+    groupCloudIdAttribute = config.getGroupCloudIdAttribute();
 		groupHierarchyLevels = config.getGroupHierarchyLevels();
 
     extendedGroupSearchFilter =  "(&"  + extendedGroupSearchFilter + "(|(" + groupMemberAttributeName + "={0})(" + groupMemberAttributeName + "={1})))";
@@ -263,9 +304,14 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 
     Set<String> groupSearchAttributes = new HashSet<String>();
     groupSearchAttributes.add(groupNameAttribute);
+    groupSearchAttributes.add(groupCloudIdAttribute);
     groupSearchAttributes.add(groupMemberAttributeName);
     groupSearchAttributes.add("uSNChanged");
     groupSearchAttributes.add("modifytimestamp");
+    otherGroupAttributes = config.getOtherGroupAttributes();
+    for (String otherGroupAttribute : otherGroupAttributes) {
+		groupSearchAttributes.add(otherGroupAttribute);
+    }
     groupSearchControls.setReturningAttributes(groupSearchAttributes.toArray(
 			new String[groupSearchAttributes.size()]));
 
@@ -284,6 +330,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 					+ ",  userNameAttribute: " + userNameAttribute
 					+ ",  userSearchAttributes: " + userSearchAttributes
           + ",  userGroupNameAttributeSet: " + userGroupNameAttributeSet
+			+ ",  otherUserAttributes: " + otherUserAttributes
           + ",  pagedResultsEnabled: " + pagedResultsEnabled
           + ",  pagedResultsSize: " + pagedResultsSize
           + ",  groupSearchEnabled: " + groupSearchEnabled
@@ -325,6 +372,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 		LOG.info("LdapDeltaUserGroupBuilder updateSink started");
 		groupUserTable = HashBasedTable.create();
         groupNameMap = new DualHashBidiMap();
+		groupInfoMap = new HashMap<>();
 		noOfNewUsers = 0;
 		noOfNewGroups = 0;
 		noOfModifiedUsers = 0;
@@ -386,7 +434,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 			String transformGroupName = groupNameTransform(groupName);
 			LOG.debug("addOrUpdateGroup(): group = " + groupName + " users = " + userList);
 			try {
-				sink.addOrUpdateGroup(transformGroupName, userList);
+				sink.addOrUpdateGroup(transformGroupName, groupInfoMap.get(groupName), userList);
 			} catch (Throwable t) {
 				LOG.error("sink.addOrUpdateGroup failed with exception: " + t.getMessage()
 				+ ", for group: " + transformGroupName
@@ -519,10 +567,22 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 							}
 						}
 
+						Map<String, String> userAttrMap = new HashMap<>();
+						Attribute userCloudIdAttr = attributes.get(userCloudIdAttribute);
+						if (userCloudIdAttr != null) {
+							addToAttrMap(userAttrMap, "cloud_id", userCloudIdAttr, config.getUserCloudIdAttributeDataType());
+						}
+						for (String otherUserAttribute : otherUserAttributes) {
+							if (attributes.get(otherUserAttribute) != null) {
+								String attrType = config.getOtherUserAttributeDataType(otherUserAttribute);
+								addToAttrMap(userAttrMap, otherUserAttribute, attributes.get(otherUserAttribute), attrType);
+							}
+						}
+
 						if (!groupSearchFirstEnabled) {
 							String transformUserName = userNameTransform(userName);
 							try {
-								sink.addOrUpdateUser(transformUserName);
+								sink.addOrUpdateUser(transformUserName, userAttrMap, null);
 							} catch (Throwable t) {
 								LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage()
 								+ ", for user: " + transformUserName);
@@ -554,7 +614,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 
 							List<String> groupList = new ArrayList<String>(groups);
 							try {
-								sink.addOrUpdateUser(transformUserName, groupList);
+								sink.addOrUpdateUser(transformUserName, userAttrMap, groupList);
 
 							} catch (Throwable t) {
 								LOG.error("sink.addOrUpdateUserGroups failed with exception: " + t.getMessage()
@@ -569,7 +629,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 								if (!userNameMap.containsKey(userFullName)) {
 									String transformUserName = userNameTransform(userName);
 									try {
-										sink.addOrUpdateUser(transformUserName);
+										sink.addOrUpdateUser(transformUserName, userAttrMap, null);
 									} catch (Throwable t) {
 										LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage()
 												+ ", for user: " + transformUserName);
@@ -640,7 +700,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 				LOG.info("LdapDeltaUserGroupBuilder.getUsers() completed with user count: "
 						+ counter);
 				} catch (Exception t) {
-					LOG.error("LdapDeltaUserGroupBuilder.getUsers() failed with exception: " + t);
+					LOG.error("LdapDeltaUserGroupBuilder.getUsers() failed with exception: ", t);
 					LOG.info("LdapDeltaUserGroupBuilder.getUsers() user count: "
 							+ counter);
 				}
@@ -704,7 +764,8 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 								continue;
 							}
 							counter++;
-							Attribute groupNameAttr = groupEntry.getAttributes().get(groupNameAttribute);
+							Attributes attributes =   groupEntry.getAttributes();
+							Attribute groupNameAttr = attributes.get(groupNameAttribute);
 							if (groupNameAttr == null) {
 								if (LOG.isInfoEnabled())  {
 									LOG.info(groupNameAttribute + " empty for entry " + groupEntry.getNameInNamespace() +
@@ -714,15 +775,27 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 							}
 							String gName = (String) groupNameAttr.get();
 							String transformGroupName = groupNameTransform(gName);
+							Map<String, String> groupAttrMap = new HashMap<>();
+							Attribute groupCloudIdAttr = attributes.get(groupCloudIdAttribute);
+							if (groupCloudIdAttr != null) {
+								addToAttrMap(groupAttrMap, "cloud_id", groupCloudIdAttr, config.getGroupCloudIdAttributeDataType());
+							}
+							for (String otherGroupAttribute : otherGroupAttributes) {
+								if (attributes.get(otherGroupAttribute) != null) {
+									String attrType = config.getOtherGroupAttributeDataType(otherGroupAttribute);
+									addToAttrMap(groupAttrMap, otherGroupAttribute, attributes.get(otherGroupAttribute), attrType);
+								}
+							}
+							groupInfoMap.put(gName, groupAttrMap);
 							// If group based search is enabled, then
 							// update the group name to ranger admin
 							// check for group members and populate userInfo object with user's full name and group mapping
 							if (groupSearchFirstEnabled) {
 								LOG.debug("Update Ranger admin with " + transformGroupName);
-								sink.addOrUpdateGroup(transformGroupName);
+								sink.addOrUpdateGroup(transformGroupName, groupAttrMap);
 							}
 
-							Attribute timeStampAttr  = groupEntry.getAttributes().get("uSNChanged");
+							Attribute timeStampAttr  = attributes.get("uSNChanged");
 							if (timeStampAttr != null) {
 								String uSNChangedVal = (String) timeStampAttr.get();
 								long currentDeltaSyncTime = Long.parseLong(uSNChangedVal);
@@ -730,7 +803,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 									highestdeltaSyncGroupTime = currentDeltaSyncTime;
 								}
 							} else {
-								timeStampAttr = groupEntry.getAttributes().get("modifytimestamp");
+								timeStampAttr = attributes.get("modifytimestamp");
 								if (timeStampAttr != null) {
 									String timeStampVal = (String) timeStampAttr.get();
 									Date parseDate = dateFormat.parse(timeStampVal);
@@ -742,7 +815,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 									}
 								}
 							}
-							Attribute groupMemberAttr = groupEntry.getAttributes().get(groupMemberAttributeName);
+							Attribute groupMemberAttr = attributes.get(groupMemberAttributeName);
 							int userCount = 0;
 							if (groupMemberAttr == null || groupMemberAttr.size() <= 0) {
 								LOG.info("No members available for " + gName);
@@ -769,7 +842,8 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 								if (groupSearchFirstEnabled && !userSearchEnabled) {
 									String transformUserName = userNameTransform(userName);
 									try {
-										sink.addOrUpdateUser(transformUserName);
+										Map<String, String> userAttrMap = new HashMap<>();
+										sink.addOrUpdateUser(transformUserName, userAttrMap, null);
 									} catch (Throwable t) {
 										LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage()
 										+ ", for user: " + transformUserName);
@@ -789,6 +863,8 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 								}
                                 groupNameMap.put(groupEntry.getNameInNamespace().toLowerCase(), gName);
 							}
+
+
 							if (groupNames.contains(gName)) {
 								noOfModifiedGroups++;
 							} else {
@@ -1021,6 +1097,15 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 								continue;
 							}
 
+							Map<String, String> groupAttrMap = new HashMap<>();
+							for (String otherGroupAttribute : otherGroupAttributes) {
+								Attribute otherGroupAttr = groupEntry.getAttributes().get(otherGroupAttribute);
+								if (otherGroupAttr != null) {
+									groupAttrMap.put(otherGroupAttribute, (String) otherGroupAttr.get());
+								}
+							}
+							groupInfoMap.put(gName, groupAttrMap);
+
 							NamingEnumeration<?> userEnum = groupMemberAttr.getAll();
 							while (userEnum.hasMore()) {
 								String originalUserFullName = (String) userEnum.next();
@@ -1035,6 +1120,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
                                     groupUserTable.put(gName, originalUserFullName, originalUserFullName);
                                 }
 								groupNameMap.put(groupEntry.getNameInNamespace().toLowerCase(), gName);
+
 							}
 							LOG.info("No. of members in the group " + gName + " = " + userCount);
 						}
@@ -1086,5 +1172,22 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
 		}
 		goUpGroupHierarchyLdap(nextLevelGroups, groupHierarchyLevels-1);
 	}
-}
 
+	private void addToAttrMap(Map<String, String> userAttrMap, String attrName, Attribute attr, String attrType) throws Throwable{
+		if (attrType.equals(DATA_TYPE_BYTEARRAY)) {
+			try {
+				byte[] otherUserAttrBytes = (byte[]) attr.get();
+				//Convert objectGUID into string and add to userAttrMap
+				String attrVal = UUID.nameUUIDFromBytes(otherUserAttrBytes).toString();
+				userAttrMap.put(attrName, attrVal);
+			} catch (ClassCastException e) {
+				LOG.error(attrName + " type is not set properly " + e.getMessage());
+			}
+		} else if (attrType.equals("String")) {
+			userAttrMap.put(attrName, (String) attr.get());
+		} else {
+			// This should not be reached.
+			LOG.warn("Attribute Type " + attrType + " not supported for " + attrName);
+		}
+	}
+}
diff --git a/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapPolicyMgrUserGroupBuilder.java b/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapPolicyMgrUserGroupBuilder.java
index 8017395..31b4d3d 100644
--- a/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapPolicyMgrUserGroupBuilder.java
+++ b/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapPolicyMgrUserGroupBuilder.java
@@ -152,25 +152,14 @@ private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder
 
 	@Override
 	public void addOrUpdateUser(String userName, List<String> groups) throws Throwable {
-		//* Add user to groups mapping in the x_user table. 
-		//* Here the assumption is that the user already exists in x_portal_user table.
-		if ( ! isMockRun ) {
-			// If the rest call to ranger admin fails, 
-			// propagate the failure to the caller for retry in next sync cycle.
-			if (addUserGroupInfo(userName, groups) == null ) {
-				String msg = "Failed to add addorUpdate user group info";
-				LOG.error(msg);
-				throw new Exception(msg);
-			}
-		}
 
 	}
 
 	@Override
-	public void addOrUpdateGroup(String groupName) throws Throwable {
+	public void addOrUpdateGroup(String groupName, Map<String, String> groupAttrs) throws Throwable {
 		//* Build the group info object and do the rest call
 			if ( ! isMockRun ) {
-				if ( addGroupInfo(groupName) == null) {
+				if ( addGroupInfo(groupName, groupAttrs) == null) {
 					String msg = "Failed to add addorUpdate group info";
 					LOG.error(msg);
 					throw new Exception(msg);
@@ -179,13 +168,13 @@ private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder
 
 	}
 	
-	private XGroupInfo addGroupInfo(final String groupName){
+	private XGroupInfo addGroupInfo(final String groupName, Map<String, String> groupAttrs){
 		XGroupInfo ret = null;
 		XGroupInfo group = null;
 		
 		LOG.debug("INFO: addPMXAGroup(" + groupName + ")");
 		if (! isMockRun) {
-			group = addXGroupInfo(groupName);
+			group = addXGroupInfo(groupName, groupAttrs);
 		}
 		if (authenticationType != null && AUTH_KERBEROS.equalsIgnoreCase(authenticationType) && SecureClientLogin.isKerberosCredentialExists(principal,keytab)) {
 			try {
@@ -213,7 +202,7 @@ private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder
 		}	
 	}
 	
-	private XGroupInfo addXGroupInfo(String aGroupName) {
+	private XGroupInfo addXGroupInfo(String aGroupName, Map<String, String> groupAttrs) {
 		
 		XGroupInfo addGroup = new XGroupInfo();
 		
@@ -224,6 +213,8 @@ private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder
 		addGroup.setGroupType("1");
 
 		addGroup.setGroupSource(GROUP_SOURCE_EXTERNAL);
+		Gson gson = new Gson();
+		addGroup.setOtherAttributes(gson.toJson(groupAttrs));
 		groupuserInfo.setXgroupInfo(addGroup);
 
 		return addGroup;
@@ -272,30 +263,33 @@ private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder
 
 	@Override
 	public void addOrUpdateUser(String userName) throws Throwable {
+
+	}
+
+	@Override
+	public void addOrUpdateUser(String userName, Map<String, String> userAttrs, List<String> groups) throws Throwable {
 		// First add to x_portal_user
 		LOG.debug("INFO: addPMAccount(" + userName + ")" );
 		if (! isMockRun) {
-			if (addMUser(userName) == null) {
-		        String msg = "Failed to add portal user";
-		        LOG.error(msg);
-		        throw new Exception(msg);
+			if (addMUser(userName, userAttrs) == null) {
+				String msg = "Failed to add portal user";
+				LOG.error(msg);
+				throw new Exception(msg);
 			}
 		}
-		List<String> groups = new ArrayList<String>();
 		//* Build the user group info object with empty groups and do the rest call
 		if ( ! isMockRun ) {
-			// If the rest call to ranger admin fails, 
+			// If the rest call to ranger admin fails,
 			// propagate the failure to the caller for retry in next sync cycle.
-			if (addUserGroupInfo(userName, groups) == null ) {
+			if (addUserGroupInfo(userName, userAttrs, groups) == null ) {
 				String msg = "Failed to add addorUpdate user group info";
 				LOG.error(msg);
 				throw new Exception(msg);
 			}
 		}
-		
 	}
-	
-	private UserGroupInfo addUserGroupInfo(String userName, List<String> groups){
+
+	private UserGroupInfo addUserGroupInfo(String userName, Map<String, String> userAttrs, List<String> groups){
 		if(LOG.isDebugEnabled()) {
 	 		LOG.debug("==> LdapPolicyMgrUserGroupBuilder.addUserGroupInfo " + userName + " and groups");
 	 	}
@@ -304,7 +298,7 @@ private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder
 		LOG.debug("INFO: addPMXAUser(" + userName + ")");
 		
 		if (! isMockRun) {
-			user = addXUserInfo(userName);
+			user = addXUserInfo(userName, userAttrs);
 		}
 		for(String g : groups) {
 				LOG.debug("INFO: addPMXAGroupToUser(" + userName + "," + g + ")" );
@@ -337,13 +331,15 @@ private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder
 		}
 	}
 	
-	private XUserInfo addXUserInfo(String aUserName) {
+	private XUserInfo addXUserInfo(String aUserName, Map<String, String> userAttrs) {
 		
 		XUserInfo xuserInfo = new XUserInfo();
 
 		xuserInfo.setName(aUserName);
 		
 		xuserInfo.setDescription(aUserName + " - add from Unix box");
+		Gson gson = new Gson();
+		xuserInfo.setOtherAttributes(gson.toJson(userAttrs));
         if (userMap.containsKey(aUserName)) {
             List<String> roleList = new ArrayList<String>();
             roleList.add(userMap.get(aUserName));
@@ -359,7 +355,7 @@ private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder
 		List<XGroupInfo> xGroupInfoList = new ArrayList<XGroupInfo>();
 		
 		for(String groupName : aGroupList) {
-			XGroupInfo group = addXGroupInfo(groupName);
+			XGroupInfo group = addXGroupInfo(groupName, null);
 			xGroupInfoList.add(group);
 			addXUserGroupInfo(aUserInfo, group);
 		}
@@ -421,6 +417,10 @@ private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder
 
 	@Override
 	public void addOrUpdateGroup(String groupName, List<String> users) throws Throwable {
+	}
+
+	@Override
+	public void addOrUpdateGroup(String groupName, Map<String, String> groupAttrs, List<String> users) throws Throwable {
 		// First get the existing group user mappings from Ranger admin.
 		// Then compute the delta and send the updated group user mappings to ranger admin.
 		LOG.debug("addOrUpdateGroup for " + groupName + " with users: " + users);
@@ -484,7 +484,7 @@ private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder
 		if ( ! isMockRun ) {
 			// If the rest call to ranger admin fails, 
 			// propagate the failure to the caller for retry in next sync cycle.
-			if (addGroupUserInfo(groupName, addUsers) == null ) {
+			if (addGroupUserInfo(groupName, groupAttrs, addUsers) == null ) {
 				String msg = "Failed to add addorUpdate group user info";
 				LOG.error(msg);
 				throw new Exception(msg);
@@ -654,7 +654,7 @@ private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder
 		}
 	}
 	
-	private GroupUserInfo addGroupUserInfo(String groupName, List<String> users){
+	private GroupUserInfo addGroupUserInfo(String groupName, Map<String, String> groupAttrs, List<String> users){
 		if(LOG.isDebugEnabled()) {
 	 		LOG.debug("==> LdapPolicyMgrUserGroupBuilder.addGroupUserInfo " + groupName + " and " + users);
 	 	}
@@ -663,7 +663,7 @@ private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder
 		
 		LOG.debug("INFO: addPMXAGroup(" + groupName + ")" );
 		if (! isMockRun) {
-			group = addXGroupInfo(groupName);
+			group = addXGroupInfo(groupName, groupAttrs);
 		}
 		for(String u : users) {
 				LOG.debug("INFO: addPMXAGroupToUser(" + groupName + "," + u + ")" );
@@ -701,7 +701,7 @@ private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder
 		List<XUserInfo> xUserInfoList = new ArrayList<XUserInfo>();
 
 		for(String userName : aUserList) {
-			XUserInfo user = addXUserInfo(userName);
+			XUserInfo user = addXUserInfo(userName, null);
 			xUserInfoList.add(user);
 			addXUserGroupInfo(user, aGroupInfo);
 		}
@@ -772,13 +772,15 @@ private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder
 	}
 
 	
-	private MUserInfo addMUser(String aUserName) {
+	private MUserInfo addMUser(String aUserName, Map<String, String> userAttrs) {
 		MUserInfo ret = null;
 		MUserInfo userInfo = new MUserInfo();
 
 		userInfo.setLoginId(aUserName);
 		userInfo.setFirstName(aUserName);
         userInfo.setLastName(aUserName);
+		Gson gson = new Gson();
+        userInfo.setOtherAttributes(gson.toJson(userAttrs));
         String str[] = new String[1];
         if (userMap.containsKey(aUserName)) {
             str[0] = userMap.get(aUserName);
diff --git a/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapUserGroupBuilder.java b/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapUserGroupBuilder.java
index 8efa161..07cba9e 100644
--- a/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapUserGroupBuilder.java
+++ b/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapUserGroupBuilder.java
@@ -721,10 +721,10 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource {
 									int userCount = 0;
 									if (groupMemberAttr == null || groupMemberAttr.size() <= 0) {
 										LOG.info("No members available for " + gName);
-										sink.addOrUpdateGroup(gName, null);
+										sink.addOrUpdateGroup(gName, new HashMap<String, String>(), null);
 										continue;
 									}
-									sink.addOrUpdateGroup(gName);
+									sink.addOrUpdateGroup(gName, new HashMap<String, String>());
 									NamingEnumeration<?> userEnum = groupMemberAttr.getAll();
 									while (userEnum.hasMore()) {
 										String originalUserFullName = (String) userEnum.next();
diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/config/UserGroupSyncConfig.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/config/UserGroupSyncConfig.java
index ae4e474..f9f51d9 100644
--- a/ugsync/src/main/java/org/apache/ranger/unixusersync/config/UserGroupSyncConfig.java
+++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/config/UserGroupSyncConfig.java
@@ -146,6 +146,15 @@ public class UserGroupSyncConfig  {
 	private static final String LGSYNC_USER_GROUP_NAME_ATTRIBUTE = "ranger.usersync.ldap.user.groupnameattribute";
 	private static final String DEFAULT_USER_GROUP_NAME_ATTRIBUTE = "memberof,ismemberof";
 
+	private static final String LGSYNC_USER_CLOUDID_ATTRIBUTE = "ranger.usersync.ldap.user.cloudid.attribute";
+	private static final String DEFAULT_USER_CLOUDID_ATTRIBUTE = "objectid";
+
+	private static final String LGSYNC_USER_CLOUDID_ATTRIBUTE_DATATYPE = "ranger.usersync.ldap.user.cloudid.attribute.datatype";
+	private static final String DEFAULT_USER_CLOUDID_ATTRIBUTE_DATATYPE = "byte[]";
+
+	private static final String LGSYNC_OTHER_USER_ATTRIBUTES = "ranger.usersync.ldap.user.otherattributes";
+	private static final String DEFAULT_OTHER_USER_ATTRIBUTES = "userurincipaluame,";
+
 	public static final String UGSYNC_NONE_CASE_CONVERSION_VALUE = "none";
 	public static final String UGSYNC_LOWER_CASE_CONVERSION_VALUE = "lower";
 	public static final String UGSYNC_UPPER_CASE_CONVERSION_VALUE = "upper";
@@ -195,6 +204,15 @@ public class UserGroupSyncConfig  {
 	private static final String LGSYNC_GROUP_MEMBER_ATTRIBUTE_NAME = "ranger.usersync.group.memberattributename";
 	private static final String DEFAULT_LGSYNC_GROUP_MEMBER_ATTRIBUTE_NAME = "member";
 
+	private static final String LGSYNC_GROUP_CLOUDID_ATTRIBUTE = "ranger.usersync.ldap.group.cloudid.attribute";
+	private static final String DEFAULT_GROUP_CLOUDID_ATTRIBUTE = "objectid";
+
+	private static final String LGSYNC_GROUP_CLOUDID_ATTRIBUTE_DATATYPE = "ranger.usersync.ldap.group.cloudid.attribute.datatype";
+	private static final String DEFAULT_GROUP_CLOUDID_ATTRIBUTE_DATATYPE = "byte[]";
+
+	private static final String LGSYNC_OTHER_GROUP_ATTRIBUTES = "ranger.usersync.ldap.group.otherattributes";
+	private static final String DEFAULT_OTHER_GROUP_ATTRIBUTES = "displayname,";
+
 	private static final String LGSYNC_GROUP_HIERARCHY_LEVELS = "ranger.usersync.ldap.grouphierarchylevels";
 	private static final int DEFAULT_LGSYNC_GROUP_HIERARCHY_LEVELS = 0;
 
@@ -648,6 +666,43 @@ public class UserGroupSyncConfig  {
 		return userGroupNameAttributeSet;
 	}
 
+	public Set<String> getOtherUserAttributes() {
+		String otherAttributes =  prop.getProperty(LGSYNC_OTHER_USER_ATTRIBUTES);
+		if(otherAttributes == null || otherAttributes.trim().isEmpty()) {
+			otherAttributes = DEFAULT_OTHER_USER_ATTRIBUTES;
+		}
+		StringTokenizer st = new StringTokenizer(otherAttributes, ",");
+		Set<String> otherUserAttributes = new HashSet<String>();
+		while (st.hasMoreTokens()) {
+			otherUserAttributes.add(st.nextToken().trim());
+		}
+		return otherUserAttributes;
+	}
+
+	public String getUserCloudIdAttribute() {
+		String val =  prop.getProperty(LGSYNC_USER_CLOUDID_ATTRIBUTE);
+		if (val == null || val.trim().isEmpty()) {
+			return DEFAULT_USER_CLOUDID_ATTRIBUTE;
+		}
+		return val;
+	}
+
+	public String getUserCloudIdAttributeDataType() {
+		String val =  prop.getProperty(LGSYNC_USER_CLOUDID_ATTRIBUTE_DATATYPE);
+		if (val == null || val.trim().isEmpty()) {
+			return DEFAULT_USER_CLOUDID_ATTRIBUTE_DATATYPE;
+		}
+		return val;
+	}
+
+	public String getOtherUserAttributeDataType(String attrName) {
+		String attrType =  prop.getProperty(LGSYNC_OTHER_USER_ATTRIBUTES + "." + attrName + "datatype");
+		if (attrType == null || attrType.isEmpty()) {
+			attrType = "String";
+		}
+		return attrType.trim();
+	}
+
 	public String getUserNameCaseConversion() {
 		String ret = prop.getProperty(UGSYNC_USERNAME_CASE_CONVERSION_PARAM, DEFAULT_UGSYNC_USERNAME_CASE_CONVERSION_VALUE);
 		return ret.trim().toLowerCase();
@@ -789,6 +844,43 @@ public class UserGroupSyncConfig  {
 		return val;
 	}
 
+	public String getGroupCloudIdAttribute() {
+		String val =  prop.getProperty(LGSYNC_GROUP_CLOUDID_ATTRIBUTE);
+		if (val == null || val.trim().isEmpty()) {
+			return DEFAULT_GROUP_CLOUDID_ATTRIBUTE;
+		}
+		return val;
+	}
+
+	public String getGroupCloudIdAttributeDataType() {
+		String val =  prop.getProperty(LGSYNC_GROUP_CLOUDID_ATTRIBUTE_DATATYPE);
+		if (val == null || val.trim().isEmpty()) {
+			return DEFAULT_GROUP_CLOUDID_ATTRIBUTE_DATATYPE;
+		}
+		return val;
+	}
+
+	public Set<String> getOtherGroupAttributes() {
+		String otherAttributes =  prop.getProperty(LGSYNC_OTHER_GROUP_ATTRIBUTES);
+		if(otherAttributes == null || otherAttributes.trim().isEmpty()) {
+			otherAttributes = DEFAULT_OTHER_GROUP_ATTRIBUTES;
+		}
+		StringTokenizer st = new StringTokenizer(otherAttributes, ",");
+		Set<String> otherGroupAttributes = new HashSet<String>();
+		while (st.hasMoreTokens()) {
+			otherGroupAttributes.add(st.nextToken().trim());
+		}
+		return otherGroupAttributes;
+	}
+
+	public String getOtherGroupAttributeDataType(String attrName) {
+                String attrType =  prop.getProperty(LGSYNC_OTHER_GROUP_ATTRIBUTES + "." + attrName + "datatype");
+                if (attrType == null || attrType.isEmpty()) {
+                        attrType = "String";
+                }
+                return attrType.trim();
+        }
+
 	public int getGroupHierarchyLevels() {
         	int groupHierarchyLevels;
         	String val = prop.getProperty(LGSYNC_GROUP_HIERARCHY_LEVELS);
diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/model/MUserInfo.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/model/MUserInfo.java
index 841bac6..4f865af 100644
--- a/ugsync/src/main/java/org/apache/ranger/unixusersync/model/MUserInfo.java
+++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/model/MUserInfo.java
@@ -26,6 +26,7 @@ public class MUserInfo {
 	private String lastName;
 	private String emailAddress;
 	private String[] userRoleList = { "ROLE_USER" };
+	private String otherAttributes;
 	
 	
 	public String getLoginId() {
@@ -58,5 +59,12 @@ public class MUserInfo {
 	public void setUserRoleList(String[] userRoleList) {
 		this.userRoleList = userRoleList;
 	}
-	
+
+	public String getOtherAttributes() {
+		return otherAttributes;
+	}
+
+	public void setOtherAttributes(String otherAttributes) {
+		this.otherAttributes = otherAttributes;
+	}
 }
diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/model/XGroupInfo.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/model/XGroupInfo.java
index cbe0eb0..b61f39c 100644
--- a/ugsync/src/main/java/org/apache/ranger/unixusersync/model/XGroupInfo.java
+++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/model/XGroupInfo.java
@@ -26,6 +26,7 @@ public class XGroupInfo {
 	private String description;
 	private String groupType;
 	private String groupSource;
+	private String otherAttributes;
 	public String getId() {
 		return id;
 	}
@@ -57,7 +58,15 @@ public class XGroupInfo {
 	public void setGroupSource(String groupSource) {
 		this.groupSource = groupSource;
 	}
-	
+
+	public String getOtherAttributes() {
+		return otherAttributes;
+	}
+
+	public void setOtherAttributes(String otherAttributes) {
+		this.otherAttributes = otherAttributes;
+	}
+
 	@Override
 	public boolean equals(Object o) {
 		if (this == o) return true;
diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/model/XUserInfo.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/model/XUserInfo.java
index e2f36b2..bee6323 100644
--- a/ugsync/src/main/java/org/apache/ranger/unixusersync/model/XUserInfo.java
+++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/model/XUserInfo.java
@@ -26,6 +26,7 @@ public class XUserInfo {
 	private String id;
 	private String name;
 	private String 	description;
+	private String otherAttributes;
     private List<String> groupNameList = new ArrayList<String>();
     private List<String> userRoleList = new ArrayList<String>();
 	
@@ -74,7 +75,15 @@ public class XUserInfo {
         this.userRoleList = userRoleList;
     }
 
-    @Override
+	public String getOtherAttributes() {
+		return otherAttributes;
+	}
+
+	public void setOtherAttributes(String otherAttributes) {
+		this.otherAttributes = otherAttributes;
+	}
+
+	@Override
     public String toString() {
         return "XUserInfo [id=" + id + ", name=" + name + ", description="
                 + description + ", groupNameList=" + groupNameList
diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
index f08c511..7402b69 100644
--- a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
+++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
@@ -1374,7 +1374,7 @@ public class PolicyMgrUserGroupBuilder implements UserGroupSink {
 	}
 
 	@Override
-	public void addOrUpdateGroup(String groupName) throws Throwable{
+	public void addOrUpdateGroup(String groupName, Map<String, String> groupAttrs) throws Throwable{
 		XGroupInfo group = groupName2XGroupInfoMap.get(groupName);
 
 		if (group == null) {    // Does not exists
@@ -1478,7 +1478,7 @@ public class PolicyMgrUserGroupBuilder implements UserGroupSink {
 				newGroupList.add(groupName);
 			}
 		}
-		addOrUpdateGroup(groupName);
+		addOrUpdateGroup(groupName, new HashMap<String, String>());
 
 	}
 
@@ -1657,4 +1657,14 @@ public class PolicyMgrUserGroupBuilder implements UserGroupSink {
             }
         }
     }
+
+	@Override
+	public void addOrUpdateUser(String user, Map<String, String> userAttrs, List<String> groups) throws Throwable {
+
+	}
+
+	@Override
+	public void addOrUpdateGroup(String group, Map<String, String> groupAttrs, List<String> users) throws Throwable {
+
+	}
 }
diff --git a/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserGroupSink.java b/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserGroupSink.java
index 77bd016..368c4f8 100644
--- a/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserGroupSink.java
+++ b/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserGroupSink.java
@@ -22,6 +22,7 @@
 import org.apache.ranger.unixusersync.model.UgsyncAuditInfo;
 
 import java.util.List;
+import java.util.Map;
 
 public interface UserGroupSink {
 	void init() throws Throwable;
@@ -30,9 +31,13 @@ public interface UserGroupSink {
 	
 	void addOrUpdateUser(String user) throws Throwable;
 	
-	void addOrUpdateGroup(String group) throws Throwable;
+	void addOrUpdateGroup(String group, Map<String, String> groupAttrs) throws Throwable;
 	
 	void addOrUpdateGroup(String group, List<String> users) throws Throwable;
 
 	void postUserGroupAuditInfo(UgsyncAuditInfo ugsyncAuditInfo) throws Throwable;
+
+	void addOrUpdateUser(String user, Map<String, String> userAttrs, List<String> groups) throws Throwable;
+
+	void addOrUpdateGroup(String group, Map<String, String> groupAttrs, List<String> users) throws Throwable;
 }
diff --git a/ugsync/src/test/java/org/apache/ranger/usergroupsync/LdapPolicyMgrUserGroupBuilderTest.java b/ugsync/src/test/java/org/apache/ranger/usergroupsync/LdapPolicyMgrUserGroupBuilderTest.java
index 99bc2b4..e10f632 100644
--- a/ugsync/src/test/java/org/apache/ranger/usergroupsync/LdapPolicyMgrUserGroupBuilderTest.java
+++ b/ugsync/src/test/java/org/apache/ranger/usergroupsync/LdapPolicyMgrUserGroupBuilderTest.java
@@ -21,6 +21,7 @@ package org.apache.ranger.usergroupsync;
 
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.ranger.ldapusersync.process.LdapPolicyMgrUserGroupBuilder;
@@ -36,14 +37,13 @@ public class LdapPolicyMgrUserGroupBuilderTest extends LdapPolicyMgrUserGroupBui
         }
 
         @Override
-        public void addOrUpdateUser(String user, List<String> groups) {
-                allGroups.addAll(groups);
-                //allUsers.add(user);
+        public void addOrUpdateUser(String user, Map<String, String> userAttrs, List<String> groups) {
+                allUsers.add(user);
                 //System.out.println("Username: " + user + " and associated groups: " + groups);
         }
 
         @Override
-        public void addOrUpdateGroup(String group) {
+        public void addOrUpdateGroup(String group, Map<String, String> groupAttrs) {
                 allGroups.add(group);
                 //System.out.println("Groupname: " + group);
         }
@@ -55,7 +55,7 @@ public class LdapPolicyMgrUserGroupBuilderTest extends LdapPolicyMgrUserGroupBui
         }
 
         @Override
-        public void addOrUpdateGroup(String group, List<String> users) {
+        public void addOrUpdateGroup(String group, Map<String, String> groupAttrs, List<String> users) {
         	boolean addGroup = false;
         		for (String user : users) {
         			if (allUsers.contains(user)) {
diff --git a/ugsync/src/test/java/org/apache/ranger/usergroupsync/PolicyMgrUserGroupBuilderTest.java b/ugsync/src/test/java/org/apache/ranger/usergroupsync/PolicyMgrUserGroupBuilderTest.java
index 2bc3951..b0ce872 100644
--- a/ugsync/src/test/java/org/apache/ranger/usergroupsync/PolicyMgrUserGroupBuilderTest.java
+++ b/ugsync/src/test/java/org/apache/ranger/usergroupsync/PolicyMgrUserGroupBuilderTest.java
@@ -19,9 +19,7 @@
 
 package org.apache.ranger.usergroupsync;
 
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
 import org.apache.ranger.unixusersync.process.PolicyMgrUserGroupBuilder;
 
@@ -43,13 +41,13 @@ public class PolicyMgrUserGroupBuilderTest extends PolicyMgrUserGroupBuilder {
         }
 
         @Override
-        public void addOrUpdateGroup(String group) {
+        public void addOrUpdateGroup(String group, Map<String, String> groupAttrs ) {
                 allGroups.add(group);
         }
 
         @Override
         public void addOrUpdateGroup(String group, List<String> users) {
-                addOrUpdateGroup(group);
+                addOrUpdateGroup(group, new HashMap<String, String>());
         }
 
         public int getTotalUsers() {