RANGER-2805 : fixed for creating role with non-existing group/user failing due to concurrent threads
Signed-off-by: Mehul Parikh <mehul@apache.org>
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/RoleRefUpdater.java b/security-admin/src/main/java/org/apache/ranger/biz/RoleRefUpdater.java
index bb68e32..ff8e2ba 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/RoleRefUpdater.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/RoleRefUpdater.java
@@ -20,6 +20,7 @@
package org.apache.ranger.biz;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
@@ -29,6 +30,7 @@
import org.apache.ranger.common.MessageEnums;
import org.apache.ranger.common.RESTErrorUtil;
import org.apache.ranger.common.RangerCommonEnums;
+import org.apache.ranger.common.db.RangerTransactionSynchronizationAdapter;
import org.apache.ranger.db.RangerDaoManager;
import org.apache.ranger.db.XXRoleRefGroupDao;
import org.apache.ranger.db.XXRoleRefRoleDao;
@@ -38,13 +40,14 @@
import org.apache.ranger.entity.XXRoleRefGroup;
import org.apache.ranger.entity.XXRoleRefRole;
import org.apache.ranger.entity.XXRoleRefUser;
+import org.apache.ranger.entity.XXTrxLog;
import org.apache.ranger.entity.XXUser;
import org.apache.ranger.plugin.model.RangerRole;
import org.apache.ranger.service.RangerAuditFields;
+import org.apache.ranger.service.RangerTransactionService;
import org.apache.ranger.service.XGroupService;
import org.apache.ranger.service.XUserService;
import org.apache.ranger.view.VXGroup;
-import org.apache.ranger.view.VXUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -71,6 +74,15 @@
@Autowired
XGroupService xGroupService;
+ @Autowired
+ RangerTransactionSynchronizationAdapter rangerTransactionSynchronizationAdapter;
+
+ @Autowired
+ RangerTransactionService transactionService;
+
+ @Autowired
+ RangerBizUtil xaBizUtil;
+
public void createNewRoleMappingForRefTable(RangerRole rangerRole, Boolean createNonExistUserGroup) throws Exception {
if (rangerRole == null) {
return;
@@ -99,29 +111,40 @@
if (StringUtils.isBlank(roleUser)) {
continue;
}
- VXUser vXUser = null;
+ Long userId = null;
XXUser xUser = daoMgr.getXXUser().findByUserName(roleUser);
if (xUser == null) {
if (createNonExistUserGroup) {
LOG.warn("User specified in role does not exist in ranger admin, creating new user, User = "
+ roleUser);
- vXUser = xUserMgr.createExternalUser(roleUser);
+ // Schedule another transaction and let this transaction complete (and commit) successfully!
+ final RoleUserCreateContext roleUserCreateContext = new RoleUserCreateContext(roleUser, roleId);
+ Runnable CreateAndAssociateUser = new Runnable () {
+ @Override
+ public void run() {
+ Runnable realTask = new Runnable () {
+ @Override
+ public void run() {
+ doCreateAndAssociateRoleUser(roleUserCreateContext);
+ }
+ };
+ transactionService.scheduleToExecuteInOwnTransaction(realTask, 0L);
+ }
+ };
+ rangerTransactionSynchronizationAdapter.executeOnTransactionCommit(CreateAndAssociateUser);
+
} else {
throw restErrorUtil.createRESTException("user with name: " + roleUser + " does not exist ",
MessageEnums.INVALID_INPUT_DATA);
}
}else {
- vXUser = xUserService.populateViewBean(xUser);
+ userId = xUser.getId();
}
- XXRoleRefUser xRoleRefUser = rangerAuditFields.populateAuditFieldsForCreate(new XXRoleRefUser());
-
- xRoleRefUser.setRoleId(roleId);
- xRoleRefUser.setUserId(vXUser.getId());
- xRoleRefUser.setUserName(roleUser);
- xRoleRefUser.setUserType(0);
- daoMgr.getXXRoleRefUser().create(xRoleRefUser);
+ if(null != userId) {
+ userRoleAssociation(roleId,userId,roleUser);
+ }
}
}
@@ -131,7 +154,7 @@
if (StringUtils.isBlank(roleGroup)) {
continue;
}
- VXGroup vXGroup = null;
+ Long groupId = null;
XXGroup xGroup = daoMgr.getXXGroup().findByGroupName(roleGroup);
if (xGroup == null) {
@@ -140,23 +163,36 @@
+ roleGroup);
VXGroup vxGroupNew = new VXGroup();
vxGroupNew.setName(roleGroup);
+ vxGroupNew.setDescription(roleGroup);
vxGroupNew.setGroupSource(RangerCommonEnums.GROUP_EXTERNAL);
- vXGroup = xUserMgr.createXGroup(vxGroupNew);
+ // Schedule another transaction and let this transaction complete (and commit) successfully!
+ final RoleGroupCreateContext roleGroupCreateContext = new RoleGroupCreateContext(vxGroupNew, roleId);
+
+ Runnable createAndAssociateRoleGroup = new Runnable() {
+ @Override
+ public void run() {
+ Runnable realTask = new Runnable() {
+ @Override
+ public void run() {
+ doCreateAndAssociateRoleGroup(roleGroupCreateContext);
+ }
+ };
+ transactionService.scheduleToExecuteInOwnTransaction(realTask, 0L);
+ }
+ };
+ rangerTransactionSynchronizationAdapter.executeOnTransactionCommit(createAndAssociateRoleGroup);
+
} else {
throw restErrorUtil.createRESTException("group with name: " + roleGroup + " does not exist ",
MessageEnums.INVALID_INPUT_DATA);
}
}else {
- vXGroup = xGroupService.populateViewBean(xGroup);
+ groupId = xGroup.getId();
}
- XXRoleRefGroup xRoleRefGroup = rangerAuditFields.populateAuditFieldsForCreate(new XXRoleRefGroup());
-
- xRoleRefGroup.setRoleId(roleId);
- xRoleRefGroup.setGroupId(vXGroup.getId());
- xRoleRefGroup.setGroupName(roleGroup);
- xRoleRefGroup.setGroupType(0);
- daoMgr.getXXRoleRefGroup().create(xRoleRefGroup);
+ if(null != groupId) {
+ groupRoleAssociation(roleId, groupId, roleGroup);
+ }
}
}
@@ -210,4 +246,174 @@
}
return true;
}
+
+ public void groupRoleAssociation(Long roleId, Long groupId, String groupName) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("===> groupRoleAssociation()");
+ }
+
+ XXRoleRefGroup xRoleRefGroup = rangerAuditFields.populateAuditFieldsForCreate(new XXRoleRefGroup());
+ xRoleRefGroup.setRoleId(roleId);
+ xRoleRefGroup.setGroupId(groupId);
+ xRoleRefGroup.setGroupName(groupName);
+ xRoleRefGroup.setGroupType(0);
+ daoMgr.getXXRoleRefGroup().create(xRoleRefGroup);
+ }
+
+ private static final class RoleGroupCreateContext {
+ final VXGroup group;
+ final Long roleId;
+
+ RoleGroupCreateContext(VXGroup group, Long roleId) {
+ this.group = group;
+ this.roleId = roleId;
+ }
+
+ @Override
+ public String toString() {
+ return "{group=" + group + ", roleId=" + roleId + "}";
+ }
+ }
+
+ void doCreateAndAssociateRoleGroup(final RoleGroupCreateContext context) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("===> doCreateAndAssociateRoleGroup()");
+ }
+ XXGroup xGroup = daoMgr.getXXGroup().findByGroupName(context.group.getName());
+
+ if (xGroup != null) {
+ groupRoleAssociation(context.roleId, xGroup.getId(), context.group.getName());
+ } else {
+ try {
+ // Create group
+ VXGroup vXGroup = xGroupService.createXGroupWithOutLogin(context.group);
+ if (null != vXGroup) {
+ List<XXTrxLog> trxLogList = xGroupService.getTransactionLog(vXGroup, "create");
+ xaBizUtil.createTrxLog(trxLogList);
+ }
+ } catch (Exception exception) {
+ LOG.error("Failed to create Group or to associate group and role, RoleGroupContext:[" + context + "]",
+ exception);
+ } finally {
+ // This transaction may still fail at commit time because another transaction
+ // has already created the group
+ // So, associate the group to role in a different transaction
+ Runnable associateRoleGroup = new Runnable() {
+ @Override
+ public void run() {
+ Runnable realTask = new Runnable() {
+ @Override
+ public void run() {
+ doAssociateRoleGroup(context);
+ }
+ };
+ transactionService.scheduleToExecuteInOwnTransaction(realTask, 0L);
+ }
+ };
+ rangerTransactionSynchronizationAdapter.executeOnTransactionCompletion(associateRoleGroup);
+ }
+ }
+ }
+
+ void doAssociateRoleGroup(final RoleGroupCreateContext context) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("===> doAssociateRoleGroup()");
+ }
+ XXGroup xGroup = daoMgr.getXXGroup().findByGroupName(context.group.getName());
+
+ if (xGroup == null) {
+ LOG.error("No Group created!! Irrecoverable error! RoleGroupContext:[" + context + "]");
+ } else {
+ try {
+ groupRoleAssociation(context.roleId, xGroup.getId(), context.group.getName());
+ } catch (Exception exception) {
+ LOG.error("Failed to associate group and role, RoleGroupContext:[" + context + "]", exception);
+ }
+ }
+ }
+
+ private static final class RoleUserCreateContext {
+ final String userName;
+ final Long roleId;
+
+ RoleUserCreateContext(String userName, Long roleId) {
+ this.userName = userName;
+ this.roleId = roleId;
+ }
+
+ @Override
+ public String toString() {
+ return "{userName=" + userName + ", roleId=" + roleId + "}";
+ }
+ }
+
+ public void userRoleAssociation(Long roleId, Long userId, String userName) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("===> userRoleAssociation()");
+ }
+ XXRoleRefUser xRoleRefUser = rangerAuditFields.populateAuditFieldsForCreate(new XXRoleRefUser());
+ xRoleRefUser.setRoleId(roleId);
+ xRoleRefUser.setUserId(userId);
+ xRoleRefUser.setUserName(userName);
+ xRoleRefUser.setUserType(0);
+ daoMgr.getXXRoleRefUser().create(xRoleRefUser);
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("<=== userRoleAssociation()");
+ }
+ }
+
+ void doCreateAndAssociateRoleUser(final RoleUserCreateContext context) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("===> doCreateAndAssociateRoleUser()");
+ }
+ XXUser xUser = daoMgr.getXXUser().findByUserName(context.userName);
+
+ if (xUser != null) {
+ userRoleAssociation(context.roleId, xUser.getId(), context.userName);
+ } else {
+ try {
+ // Create External user
+ xUserMgr.createServiceConfigUser(context.userName);
+ } catch (Exception exception) {
+ LOG.error("Failed to create User or to associate user and role, RoleUserContext:[" + context + "]",
+ exception);
+ } finally {
+ // This transaction may still fail at commit time because another transaction
+ // has already created the user
+ // So, associate the user to role in a different transaction
+ Runnable associateRoleUser = new Runnable() {
+ @Override
+ public void run() {
+ Runnable realTask = new Runnable() {
+ @Override
+ public void run() {
+ doAssociateRoleUser(context);
+ }
+ };
+ transactionService.scheduleToExecuteInOwnTransaction(realTask, 0L);
+ }
+ };
+ rangerTransactionSynchronizationAdapter.executeOnTransactionCompletion(associateRoleUser);
+ }
+ }
+
+ }
+
+ void doAssociateRoleUser(final RoleUserCreateContext context) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("===> doAssociateRoleUser()");
+ }
+ XXUser xUser = daoMgr.getXXUser().findByUserName(context.userName);
+
+ if (xUser == null) {
+ LOG.error("No User created!! Irrecoverable error! RoleUserContext:[" + context + "]");
+ } else {
+ try {
+ userRoleAssociation(context.roleId, xUser.getId(), context.userName);
+ } catch (Exception exception) {
+ LOG.error("Failed to associate user and role, RoleUserContext:[" + context + "]", exception);
+ }
+ }
+ }
+
}