Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,11 @@ public UserTwoFactorAuthenticationSetupResponse setupUserTwoFactorAuthentication
return null;
}

@Override
public List<String> getApiNameList() {
return null;
}

@Override
public boolean deleteUserAccount(long arg0) {
// TODO Auto-generated method stub
Expand Down
2 changes: 2 additions & 0 deletions server/src/main/java/com/cloud/user/AccountManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ void buildACLViewSearchCriteria(SearchCriteria<? extends ControlledViewEntity> s
UserTwoFactorAuthenticator getUserTwoFactorAuthenticator(final Long domainId, final Long userAccountId);

void verifyUsingTwoFactorAuthenticationCode(String code, Long domainId, Long userAccountId);

UserTwoFactorAuthenticationSetupResponse setupUserTwoFactorAuthentication(SetupUserTwoFactorAuthenticationCmd cmd);

List<String> getApiNameList();
}
5 changes: 5 additions & 0 deletions server/src/main/java/com/cloud/user/AccountManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,11 @@ public void setQuerySelectors(List<QuerySelector> querySelectors) {
_querySelectors = querySelectors;
}

@Override
public List<String> getApiNameList() {
return apiNameList;
}

@Override
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
_systemAccount = _accountDao.findById(Account.ACCOUNT_ID_SYSTEM);
Expand Down
103 changes: 82 additions & 21 deletions server/src/main/java/org/apache/cloudstack/acl/RoleManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;

Expand Down Expand Up @@ -382,42 +385,100 @@ public List<Role> findRolesByName(String name) {
public Pair<List<Role>, Integer> findRolesByName(String name, String keyword, Long startIndex, Long limit) {
if (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(keyword)) {
Pair<List<RoleVO>, Integer> data = roleDao.findAllByName(name, keyword, startIndex, limit, isCallerRootAdmin());
int removed = removeRootAdminRolesIfNeeded(data.first());
int removed = removeRolesIfNeeded(data.first());
return new Pair<List<Role>,Integer>(ListUtils.toListOfInterface(data.first()), Integer.valueOf(data.second() - removed));
}
return new Pair<List<Role>, Integer>(new ArrayList<Role>(), 0);
}

/**
* Removes roles of the given list that have the type '{@link RoleType#Admin}' if the user calling the method is not a 'root admin'.
* The actual removal is executed via {@link #removeRootAdminRoles(List)}. Therefore, if the method is called by a 'root admin', we do nothing here.
* Removes roles from the given list if the role has different or more permissions than the user's calling the method role
*/
protected int removeRootAdminRolesIfNeeded(List<? extends Role> roles) {
if (!isCallerRootAdmin()) {
return removeRootAdminRoles(roles);
}
return 0;
}

/**
* Remove all roles that have the {@link RoleType#Admin}.
*/
protected int removeRootAdminRoles(List<? extends Role> roles) {
if (CollectionUtils.isEmpty(roles)) {
protected int removeRolesIfNeeded(List<? extends Role> roles) {
if (roles.isEmpty()) {
return 0;
}
Iterator<? extends Role> rolesIterator = roles.iterator();

Long callerRoleId = getCurrentAccount().getRoleId();
Map<String, Permission> callerRolePermissions = getRoleRulesAndPermissions(callerRoleId);

int count = 0;
Iterator<? extends Role> rolesIterator = roles.iterator();
while (rolesIterator.hasNext()) {
Role role = rolesIterator.next();
if (RoleType.Admin == role.getRoleType()) {
count++;
rolesIterator.remove();

if (role.getId() == callerRoleId || roleHasPermission(callerRolePermissions, role)) {
continue;
}

count++;
rolesIterator.remove();
}

return count;
}

/**
* Checks if the role of the caller account has compatible permissions of the specified role.
* For each permission of the role of the caller, the target role needs to contain the same permission.
*
* @param sourceRolePermissions the permissions of the caller role.
* @param targetRole the role that the caller role wants to access.
* @return True if the role can be accessed with the given permissions; false otherwise.
*/
protected boolean roleHasPermission(Map<String, Permission> sourceRolePermissions, Role targetRole) {
Set<String> rulesAlreadyCompared = new HashSet<>();
for (RolePermission rolePermission : findAllPermissionsBy(targetRole.getId())) {
boolean permissionIsRegex = rolePermission.getRule().getRuleString().contains("*");

for (String apiName : accountManager.getApiNameList()) {
if (!rolePermission.getRule().matches(apiName) || rulesAlreadyCompared.contains(apiName)) {
continue;
}

if (rolePermission.getPermission() == Permission.ALLOW && (!sourceRolePermissions.containsKey(apiName) || sourceRolePermissions.get(apiName) == Permission.DENY)) {
return false;
}

rulesAlreadyCompared.add(apiName);

if (!permissionIsRegex) {
break;
}
}
}

return true;
}

/**
* Given a role ID, returns a {@link Map} containing the API name as the key and the {@link Permission} for the API as the value.
*
* @param roleId ID from role.
*/
public Map<String, Permission> getRoleRulesAndPermissions(Long roleId) {
Map<String, Permission> roleRulesAndPermissions = new HashMap<>();

for (RolePermission rolePermission : findAllPermissionsBy(roleId)) {
boolean permissionIsRegex = rolePermission.getRule().getRuleString().contains("*");

for (String apiName : accountManager.getApiNameList()) {
if (!rolePermission.getRule().matches(apiName)) {
continue;
}

if (!roleRulesAndPermissions.containsKey(apiName)) {
roleRulesAndPermissions.put(apiName, rolePermission.getPermission());
}

if (!permissionIsRegex) {
break;
}
}
}
return roleRulesAndPermissions;
}

@Override
public List<Role> findRolesByType(RoleType roleType) {
return findRolesByType(roleType, null, null).first();
Expand All @@ -435,14 +496,14 @@ public Pair<List<Role>, Integer> findRolesByType(RoleType roleType, Long startIn
@Override
public List<Role> listRoles() {
List<? extends Role> roles = roleDao.listAll();
removeRootAdminRolesIfNeeded(roles);
removeRolesIfNeeded(roles);
return ListUtils.toListOfInterface(roles);
}

@Override
public Pair<List<Role>, Integer> listRoles(Long startIndex, Long limit) {
Pair<List<RoleVO>, Integer> data = roleDao.listAllRoles(startIndex, limit, isCallerRootAdmin());
int removed = removeRootAdminRolesIfNeeded(data.first());
int removed = removeRolesIfNeeded(data.first());
return new Pair<List<Role>,Integer>(ListUtils.toListOfInterface(data.first()), Integer.valueOf(data.second() - removed));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -479,4 +479,8 @@ public ConfigKey<?>[] getConfigKeys() {
return null;
}

@Override
public List<String> getApiNameList() {
return null;
}
}
Loading