You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by ni...@apache.org on 2020/10/26 13:20:49 UTC
[ranger] branch master updated: RANGER-2996 : Add search by Roles
and Auditor user should be able to see Roles tab.4
This is an automated email from the ASF dual-hosted git repository.
ni3galave 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 72a3f94 RANGER-2996 : Add search by Roles and Auditor user should be able to see Roles tab.4
72a3f94 is described below
commit 72a3f9442ef7084323036c0682d5ff65b981d65e
Author: Nitin Galave <ni...@apache.org>
AuthorDate: Mon Oct 19 18:09:45 2020 +0530
RANGER-2996 : Add search by Roles and Auditor user should be able to see Roles tab.4
---
.../java/org/apache/ranger/biz/RoleDBStore.java | 59 +++++++++-
.../main/java/org/apache/ranger/db/XXRoleDao.java | 15 +++
.../main/java/org/apache/ranger/rest/RoleREST.java | 26 ++++-
.../ranger/service/RangerPolicyServiceBase.java | 2 +-
.../main/resources/META-INF/jpa_named_queries.xml | 9 ++
.../views/policymanager/ServiceLayoutSidebar.js | 3 +-
.../scripts/views/reports/UserAccessLayout.js | 125 ++++++++++++++++++---
.../webapp/scripts/views/users/UserTableLayout.js | 2 +
.../common/ServiceManagerSidebarLayout_tmpl.html | 2 +
.../templates/reports/UserAccessLayout_tmpl.html | 2 +
10 files changed, 227 insertions(+), 18 deletions(-)
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 6ef5fe5..6483bbe 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
@@ -30,9 +30,12 @@ import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig;
+import org.apache.ranger.common.ContextUtil;
import org.apache.ranger.common.MessageEnums;
import org.apache.ranger.common.RESTErrorUtil;
+import org.apache.ranger.common.RangerConstants;
import org.apache.ranger.common.RangerRoleCache;
+import org.apache.ranger.common.UserSessionBase;
import org.apache.ranger.common.db.RangerTransactionSynchronizationAdapter;
import org.apache.ranger.db.RangerDaoManager;
import org.apache.ranger.entity.*;
@@ -43,7 +46,9 @@ import org.apache.ranger.plugin.store.RoleStore;
import org.apache.ranger.plugin.util.RangerRoles;
import org.apache.ranger.plugin.util.SearchFilter;
import org.apache.ranger.service.RangerRoleService;
+import org.apache.ranger.service.XUserService;
import org.apache.ranger.view.RangerRoleList;
+import org.apache.ranger.view.VXUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -60,6 +65,9 @@ public class RoleDBStore implements RoleStore {
RangerRoleService roleService;
@Autowired
+ XUserService xUserService;
+
+ @Autowired
RangerDaoManager daoMgr;
@Autowired
@@ -282,7 +290,56 @@ public class RoleDBStore implements RoleStore {
rangerRoleList.setRoleList(roles);
return rangerRoleList;
}
-
+
+ public RangerRoleList getRolesForUser(SearchFilter filter, RangerRoleList rangerRoleList) throws Exception {
+ List<RangerRole> roles = new ArrayList<RangerRole>();
+ List<XXRole> xxRoles = null;
+ UserSessionBase userSession = ContextUtil.getCurrentUserSession();
+ if (userSession != null && userSession.getUserRoleList().size() == 1
+ && userSession.getUserRoleList().contains(RangerConstants.ROLE_USER)
+ && userSession.getLoginId() != null) {
+ VXUser loggedInVXUser = xUserService.getXUserByUserName(userSession.getLoginId());
+ xxRoles = daoMgr.getXXRole().findByUserId(loggedInVXUser.getId());
+
+ if (CollectionUtils.isNotEmpty(xxRoles)) {
+ for (XXRole xxRole : xxRoles) {
+ roles.add(roleService.read(xxRole.getId()));
+ }
+ }
+ if (predicateUtil != null && filter != null && !filter.isEmpty()) {
+ List<RangerRole> copy = new ArrayList<>(roles);
+
+ predicateUtil.applyFilter(copy, filter);
+ roles = copy;
+ }
+ int totalCount = roles.size();
+ int startIndex = filter.getStartIndex();
+ int pageSize = filter.getMaxRows();
+ int toIndex = Math.min(startIndex + pageSize, totalCount);
+ if (CollectionUtils.isNotEmpty(roles)) {
+ roles = roles.subList(startIndex, toIndex);
+ rangerRoleList.setResultSize(roles.size());
+ rangerRoleList.setPageSize(filter.getMaxRows());
+ rangerRoleList.setSortBy(filter.getSortBy());
+ rangerRoleList.setSortType(filter.getSortType());
+ rangerRoleList.setStartIndex(filter.getStartIndex());
+ rangerRoleList.setTotalCount(totalCount);
+ }
+ } else {
+ xxRoles = (List<XXRole>) roleService.searchResources(filter, roleService.searchFields,
+ roleService.sortFields, rangerRoleList);
+
+ if (CollectionUtils.isNotEmpty(xxRoles)) {
+ for (XXRole xxRole : xxRoles) {
+ roles.add(roleService.read(xxRole.getId()));
+ }
+ }
+ }
+ rangerRoleList.setRoleList(roles);
+
+ return rangerRoleList;
+ }
+
@Override
public List<String> getRoleNames(SearchFilter filter) throws Exception {
return daoMgr.getXXRole().getAllNames();
diff --git a/security-admin/src/main/java/org/apache/ranger/db/XXRoleDao.java b/security-admin/src/main/java/org/apache/ranger/db/XXRoleDao.java
index 8528652..35d7188 100644
--- a/security-admin/src/main/java/org/apache/ranger/db/XXRoleDao.java
+++ b/security-admin/src/main/java/org/apache/ranger/db/XXRoleDao.java
@@ -96,5 +96,20 @@ public class XXRoleDao extends BaseDao<XXRole> {
return new ArrayList<String>();
}
}
+
+ @SuppressWarnings("unchecked")
+ public List<XXRole> findByUserId(Long UserId) {
+ if (UserId == null) {
+ return null;
+ }
+ List<XXRole> ret;
+ try {
+ ret = getEntityManager().createNamedQuery("XXRole.findByUserId", tClass).setParameter("userId", UserId)
+ .getResultList();
+ } catch (NoResultException e) {
+ ret = ListUtils.EMPTY_LIST;
+ }
+ return ret;
+ }
}
diff --git a/security-admin/src/main/java/org/apache/ranger/rest/RoleREST.java b/security-admin/src/main/java/org/apache/ranger/rest/RoleREST.java
index be3bf2f..86cda07 100644
--- a/security-admin/src/main/java/org/apache/ranger/rest/RoleREST.java
+++ b/security-admin/src/main/java/org/apache/ranger/rest/RoleREST.java
@@ -316,7 +316,7 @@ public class RoleREST {
return ret;
}
- /* This operation is allowed only when effective User has ranger admin privilege
+ /* This operation is allowed only when effective User has ranger admin or auditor privilege
* if execUser is not same as logged-in user then effective user is execUser
* else effective user is logged-in user.
* This logic is implemented as part of ensureAdminAccess(String serviceName, String userName);
@@ -331,7 +331,6 @@ public class RoleREST {
}
SearchFilter filter = searchUtil.getSearchFilter(request, roleService.sortFields);
try {
- ensureAdminAccess(null, null);
roleStore.getRoles(filter,ret);
} catch(WebApplicationException excp) {
throw excp;
@@ -346,6 +345,29 @@ public class RoleREST {
return ret;
}
+ @GET
+ @Path("/lookup/roles")
+ public RangerRoleList getAllRolesForUser(@Context HttpServletRequest request) {
+ RangerRoleList ret = new RangerRoleList();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> getAllRolesForUser()");
+ }
+ SearchFilter filter = searchUtil.getSearchFilter(request, roleService.sortFields);
+ try {
+ roleStore.getRolesForUser(filter,ret);
+ } catch(WebApplicationException excp) {
+ throw excp;
+ } catch(Throwable excp) {
+ LOG.error("getRoles() failed", excp);
+
+ throw restErrorUtil.createRESTException(excp.getMessage());
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== getAllRoles():" + ret);
+ }
+ return ret;
+ }
+
/* This operation is allowed only when effective User has ranger admin privilege
* if execUser is not same as logged-in user then effective user is execUser
* else effective user is logged-in user.
diff --git a/security-admin/src/main/java/org/apache/ranger/service/RangerPolicyServiceBase.java b/security-admin/src/main/java/org/apache/ranger/service/RangerPolicyServiceBase.java
index bcbca09..e8c593d 100644
--- a/security-admin/src/main/java/org/apache/ranger/service/RangerPolicyServiceBase.java
+++ b/security-admin/src/main/java/org/apache/ranger/service/RangerPolicyServiceBase.java
@@ -71,7 +71,7 @@ public abstract class RangerPolicyServiceBase<T extends XXPolicyBase, V extends
"XXGroup xGrp , XXPolicyRefGroup refGroup", "obj.id = refGroup.policyId "
+ "and xGrp.id = refGroup.groupId"));
searchFields.add(new SearchField(SearchFilter.ROLE, "xRole.name", DATA_TYPE.STRING, SEARCH_TYPE.FULL,
- "XXRole xRole , XXPolicyRefRole refRole", "obj.id = reRole.policyId "
+ "XXRole xRole , XXPolicyRefRole refRole", "obj.id = refRole.policyId "
+ "and xRole.id = refRole.roleId"));
//might need updation
/*searchFields.add(new SearchField(SearchFilter.POL_RESOURCE, "resMap.value", DATA_TYPE.STRING,
diff --git a/security-admin/src/main/resources/META-INF/jpa_named_queries.xml b/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
index 0cf9295..d608ff8 100755
--- a/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
+++ b/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
@@ -1783,4 +1783,13 @@
<query>select obj from XXRMSMappingProvider obj where obj.name = :name</query>
</named-query>
+ <named-query name="XXRole.findByUserId">
+ <query>SELECT obj FROM XXRole obj
+ WHERE obj.id IN (SELECT roleRefUser.roleId FROM XXRoleRefUser roleRefUser WHERE roleRefUser.userId = :userId)
+ OR obj.id IN (SELECT roleRefGroup.roleId FROM XXRoleRefGroup roleRefGroup WHERE roleRefGroup.groupId IN
+ (SELECT groupUser.parentGroupId FROM XXGroupUser groupUser WHERE groupUser.userId = :userId))
+ order by obj.id
+ </query>
+ </named-query>
+
</entity-mappings>
diff --git a/security-admin/src/main/webapp/scripts/views/policymanager/ServiceLayoutSidebar.js b/security-admin/src/main/webapp/scripts/views/policymanager/ServiceLayoutSidebar.js
index 42722b0..67a577c 100644
--- a/security-admin/src/main/webapp/scripts/views/policymanager/ServiceLayoutSidebar.js
+++ b/security-admin/src/main/webapp/scripts/views/policymanager/ServiceLayoutSidebar.js
@@ -111,7 +111,8 @@ define(function(require){
'searchBtn' : '[data-js="searchBtn"]',
'userName' : '[data-js="userName"]',
'selectServiceName' : '[data-js="serviceName"]',
- 'profileTab' : '.profile-tab'
+ 'profileTab' : '.profile-tab',
+ 'roleName' : '[data-js="roleName"]',
},
diff --git a/security-admin/src/main/webapp/scripts/views/reports/UserAccessLayout.js b/security-admin/src/main/webapp/scripts/views/reports/UserAccessLayout.js
index df052cb..146e3f3 100644
--- a/security-admin/src/main/webapp/scripts/views/reports/UserAccessLayout.js
+++ b/security-admin/src/main/webapp/scripts/views/reports/UserAccessLayout.js
@@ -87,6 +87,7 @@ define(function(require) {'use strict';
policyLabels : '[data-id="policyLabels"]',
zoneName : '[data-id="zoneName"]',
selectUserGroup : '[data-id="btnUserGroup"]',
+ roleName : '[data-js="roleName"]',
},
@@ -169,6 +170,7 @@ define(function(require) {'use strict';
this.ui.selectUserGroup = sidebarUiElement.selectUserGroup;
this.ui.userName = sidebarUiElement.userName;
this.ui.searchBtn = sidebarUiElement.searchBtn;
+ this.ui.roleName = sidebarUiElement.roleName;
}
this.initializePlugins();
if( this.urlQueryParams) {
@@ -184,13 +186,25 @@ define(function(require) {'use strict';
this.setupUserAutoComplete();
this.ui.userGroup.select2('destroy');
this.ui.userGroup.val('').hide();
- this.ui.selectUserGroup.find('span').first().text('Username')
- } else {
+ this.ui.roleName.select2('destroy');
+ this.ui.roleName.val('').hide();
+ this.ui.selectUserGroup.text('Username')
+ } else if (!_.isUndefined(this.urlParam['group']) && !_.isEmpty(this.urlParam['group'])) {
this.ui.userGroup.show();
this.setupGroupAutoComplete();
this.ui.userName.select2('destroy');
this.ui.userName.val('').hide();
- this.ui.selectUserGroup.find('span').first().text('Group')
+ this.ui.roleName.select2('destroy');
+ this.ui.roleName.val('').hide();
+ this.ui.selectUserGroup.text('Group')
+ } else {
+ this.ui.roleName.show();
+ this.setupRoleAutoComplete();
+ this.ui.userGroup.select2('destroy');
+ this.ui.userGroup.val('').hide();
+ this.ui.userName.select2('destroy');
+ this.ui.userName.val('').hide();
+ this.ui.selectUserGroup.text('Rolename')
}
} else {
this.setupGroupAutoComplete();
@@ -263,7 +277,39 @@ define(function(require) {'use strict';
getSubgridColumns:function(coll,collName,serviceDefName){
var that = this;
- var subcolumns = [{
+ var subcolumns = [
+ {
+ name: 'roles',
+ cell: 'html',
+ label: 'Roles',
+ formatter: _.extend({}, Backgrid.CellFormatter.prototype, {
+ fromRaw: function (rawValue,model, coll) {
+ var role_str = '';
+ if(_.isEmpty(model.get('roles'))){
+ return '<center>--</center>';
+ } else {
+ _.each(model.get('roles'),function(role,index){
+ if(index < 4) {
+ role_str += '<span class="badge badge-info cellWidth-1 float-left-margin-2" role-policy-id="'+model.cid+'" style="">' + _.escape(role) + '</span>' + " ";
+ } else {
+ role_str += '<span class="badge badge-info cellWidth-1 float-left-margin-2" role-policy-id="'+model.cid+'" style="display:none">' + _.escape(role) + '</span>' + " ";
+ }
+ });
+ if(model.get('roles').length > 4) {
+ role_str += '<span class="pull-left float-left-margin-2">\
+ <a href="javascript:void(0);" data-id="showMoreAccess" policy-id="'+
+ model.cid+'"><code style=""> + More..</code></a></span>\
+ <span class="pull-left float-left-margin-2" ><a href="javascript:void(0);" data-id="showLessAccess" policy-id="'+
+ model.cid+'" style="display:none;"><code style=""> - Less..</code></a></span>';}
+ return role_str;
+ }
+ }
+ }),
+ editable: false,
+ click: false,
+ sortable: false
+ },
+ {
name: 'groups',
cell: 'html',
label: 'Groups',
@@ -633,15 +679,13 @@ define(function(require) {'use strict';
//maximumSelectionSize : 1,
value : (!_.isUndefined(that.urlParam) && !_.isUndefined(that.urlParam['serviceType']) && !_.isEmpty(that.urlParam['serviceType'])) ?
this.ui.componentType.val(that.urlParam['serviceType']) : this.ui.componentType.val(""),
- width: '220px',
allowClear: true,
data: options
});
this.ui.policyType.select2({
closeOnSelect: false,
maximumSelectionSize : 1,
- width: '220px',
- value : (!_.isUndefined(that.urlParam) && !_.isUndefined(that.urlParam['policyType']) && !_.isEmpty(that.urlParam['policyType'])) ?
+ value : (!_.isUndefined(that.urlParam) && !_.isUndefined(that.urlParam['policyType']) && !_.isEmpty(that.urlParam['policyType'])) ?
this.ui.policyType.val(that.urlParam['policyType']) : this.ui.policyType.val("0"),
allowClear: false,
data: policyTypes
@@ -650,7 +694,6 @@ define(function(require) {'use strict';
multiple: true,
closeOnSelect : true,
placeholder : 'Policy Label',
- width :'220px',
allowClear: true,
tokenSeparators: ["," , " "],
tags : true,
@@ -700,7 +743,6 @@ define(function(require) {'use strict';
this.ui.zoneName.select2({
closeOnSelect: false,
maximumSelectionSize : 1,
- width: '220px',
allowClear: true,
data: zoneListOptions,
placeholder: 'Select Zone Name',
@@ -763,7 +805,6 @@ define(function(require) {'use strict';
closeOnSelect : true,
placeholder : 'Select Group',
maximumSelectionSize : 1,
- width :'220px',
tokenSeparators: [",", " "],
allowClear: true,
// tags : this.groupArr,
@@ -810,7 +851,6 @@ define(function(require) {'use strict';
this.ui.userName.select2({
closeOnSelect : true,
placeholder : 'Select User',
- width :'220px',
allowClear: true,
initSelection : function (element, callback) {
var data = {};
@@ -851,6 +891,52 @@ define(function(require) {'use strict';
this.ui.userName.val(this.urlParam['user']).trigger('change');
}
},
+
+ setupRoleAutoComplete : function(){
+ var that = this;
+ this.ui.roleName.select2({
+ closeOnSelect : true,
+ placeholder : 'Select Role',
+ allowClear: true,
+ initSelection : function (element, callback) {
+ var data = {};
+ data = {id: element.val(), text: element.val()};
+ callback(data);
+ },
+ ajax: {
+ url: "service/roles/roles",
+ dataType: 'json',
+ data: function (term, page) {
+ return {roleNamePartial : term};
+ },
+ results: function (data, page) {
+ var results = [],selectedVals=[];
+ if(!_.isEmpty(that.ui.roleName.select2('val')))
+ selectedVals = that.ui.roleName.select2('val');
+ if(data.totalCount != "0"){
+ results = data.roles.map(function(m){ return {id : _.escape(m.name), text: _.escape(m.name) };});
+ if(!_.isEmpty(selectedVals))
+ results = XAUtil.filterResultByIds(results, selectedVals);
+ return {results : results};
+ }
+ return {results : results};
+ }
+ },
+ formatResult : function(result){
+ return result.text;
+ },
+ formatSelection : function(result){
+ return result.text;
+ },
+ formatNoMatches: function(result){
+ return 'No user found.';
+ }
+ })//.on('select2-focus', XAUtil.select2Focus);
+ if(this.urlParam && this.urlParam['role'] && !_.isEmpty(this.urlParam['role'])) {
+ this.ui.roleName.val(this.urlParam['role']).trigger('change');
+ }
+ },
+
/** all post render plugin initialization */
initializePlugins : function() {
var that = this;
@@ -890,9 +976,10 @@ define(function(require) {'use strict';
//Get search values
var groups = (this.ui.selectUserGroup.text().trim() == "Group" ) ? this.ui.userGroup.select2('val'):undefined;
var users = (this.ui.selectUserGroup.text().trim() == "Username") ? this.ui.userName.select2('val'):undefined;
+ var roles = (this.ui.selectUserGroup.text().trim() == "Rolename") ? this.ui.roleName.select2('val'):undefined;
var rxName = this.ui.resourceName.val(), policyName = this.ui.policyName.val() , policyType = this.ui.policyType.val(),
policyLabel = this.ui.policyLabels.val(), zoneName = this.ui.zoneName.val()
- var params = {group : groups, user : users, polResource : rxName, policyNamePartial : policyName, policyType: policyType, policyLabelsPartial:policyLabel,
+ var params = {group : groups, user : users, role : roles, polResource : rxName, policyNamePartial : policyName, policyType: policyType, policyLabelsPartial:policyLabel,
zoneName : zoneName};
var component = (this.ui.componentType.val() != "") ? this.ui.componentType.select2('val'):undefined;
urlParam = _.extend(params, {'serviceType': this.ui.componentType.val()});
@@ -972,12 +1059,24 @@ define(function(require) {'use strict';
this.setupGroupAutoComplete();
this.ui.userName.select2('destroy');
this.ui.userName.val('').hide();
- } else {
+ this.ui.roleName.select2('destroy');
+ this.ui.roleName.val('').hide();
+ } else if ($el.data('id')== "userSel") {
this.ui.userGroup.select2('destroy');
this.ui.userGroup.val('').hide();
this.ui.userName.show();
this.setupUserAutoComplete();
$button.text('Username');
+ this.ui.roleName.select2('destroy');
+ this.ui.roleName.val('').hide();
+ }else {
+ this.ui.userGroup.select2('destroy');
+ this.ui.userGroup.val('').hide();
+ this.ui.roleName.show();
+ this.setupRoleAutoComplete();
+ $button.text('Rolename');
+ this.ui.userName.select2('destroy');
+ this.ui.userName.val('').hide();
}
},
setDownloadFormatFilter : function(e){
diff --git a/security-admin/src/main/webapp/scripts/views/users/UserTableLayout.js b/security-admin/src/main/webapp/scripts/views/users/UserTableLayout.js
index 4d58b21..b8270d9 100755
--- a/security-admin/src/main/webapp/scripts/views/users/UserTableLayout.js
+++ b/security-admin/src/main/webapp/scripts/views/users/UserTableLayout.js
@@ -109,6 +109,7 @@ define(function(require){
}
if(_.isUndefined(this.roleList)){
this.roleList = new VXRoleList();
+ this.roleList.url = "service/roles/lookup/roles";
}
this.bindEvents();
@@ -323,6 +324,7 @@ define(function(require){
this.ui.hideShowVisibility.hide();
if(_.isUndefined(this.roleList) || _.isUndefined(this.urlQueryParams) || _.isEmpty(this.urlQueryParams)){
this.roleList = new VXRoleList();
+ this.roleList.url = "service/roles/lookup/roles";
}
this.ui.addNewUser.hide();
this.ui.addNewGroup.hide();
diff --git a/security-admin/src/main/webapp/templates/common/ServiceManagerSidebarLayout_tmpl.html b/security-admin/src/main/webapp/templates/common/ServiceManagerSidebarLayout_tmpl.html
index b61dd45..9b64a21 100644
--- a/security-admin/src/main/webapp/templates/common/ServiceManagerSidebarLayout_tmpl.html
+++ b/security-admin/src/main/webapp/templates/common/ServiceManagerSidebarLayout_tmpl.html
@@ -214,10 +214,12 @@
<ul class="dropdown-menu">
<a data-id="grpSel" class="autoText dropdown-item" href="javascript:;">Group</a>
<a data-id="userSel" class="autoText dropdown-item" href="javascript:;">Username</a>
+ <a data-id="roleSel" class="autoText dropdown-item" href="javascript:;">Rolename</a>
</ul>
</div>
<input type="text" data-js="selectGroups">
<input type="text" style="display: none" data-js="userName">
+ <input type="text" style="display: none" data-js="roleName">
</div>
</div>
</div>
diff --git a/security-admin/src/main/webapp/templates/reports/UserAccessLayout_tmpl.html b/security-admin/src/main/webapp/templates/reports/UserAccessLayout_tmpl.html
index 99d04cb..d69a6ec 100644
--- a/security-admin/src/main/webapp/templates/reports/UserAccessLayout_tmpl.html
+++ b/security-admin/src/main/webapp/templates/reports/UserAccessLayout_tmpl.html
@@ -89,10 +89,12 @@
<div class="dropdown-menu">
<a data-id="grpSel" class="autoText dropdown-item" href="javascript:;">Group</a>
<a data-id="userSel" class="autoText dropdown-item" href="javascript:;">Username</a>
+ <a data-id="roleSel" class="autoText dropdown-item" href="javascript:;">Rolename</a>
</div>
</div>
<input type="text" class="form-control" data-js="selectGroups">
<input type="text" class="form-control" style="display: none" data-js="userName">
+ <input type="text" class="form-control" style="display: none" data-js="roleName">
</div>
</div>
</div>