create parameter to determine whether roles are public or private (#6960)

Co-authored-by: Gabriel Ortiga Fernandes <gabriel.fernandes@scclouds.com.br>
Co-authored-by: dahn <daan.hoogland@gmail.com>
This commit is contained in:
GaOrtiga 2023-05-01 10:26:10 -03:00 committed by GitHub
parent 957c0a5193
commit 8b5bfb145e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 435 additions and 57 deletions

View File

@ -23,4 +23,5 @@ import org.apache.cloudstack.api.InternalIdentity;
public interface Role extends RoleEntity, InternalIdentity, Identity {
RoleType getRoleType();
boolean isDefault();
boolean isPublicRole();
}

View File

@ -38,15 +38,17 @@ public interface RoleService {
* Moreover, we will check if the requested role is of 'Admin' type; roles with 'Admin' type should only be visible to 'root admins'.
* Therefore, if a non-'root admin' user tries to search for an 'Admin' role, this method will return null.
*/
Role findRole(Long id, boolean removePrivateRoles);
Role findRole(Long id);
Role createRole(String name, RoleType roleType, String description);
Role createRole(String name, RoleType roleType, String description, boolean publicRole);
Role createRole(String name, Role role, String description);
Role createRole(String name, Role role, String description, boolean publicRole);
Role importRole(String name, RoleType roleType, String description, List<Map<String, Object>> rules, boolean forced);
Role importRole(String name, RoleType roleType, String description, List<Map<String, Object>> rules, boolean forced, boolean isPublicRole);
Role updateRole(Role role, String name, RoleType roleType, String description);
Role updateRole(Role role, String name, RoleType roleType, String description, Boolean publicRole);
boolean deleteRole(Role role);

View File

@ -48,6 +48,10 @@ public class CreateRoleCmd extends RoleCmd {
description = "ID of the role to be cloned from. Either roleid or type must be passed in")
private Long roleId;
@Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Indicates whether the role will be visible to all users (public) or only to root admins (private)." +
" If this parameter is not specified during the creation of the role its value will be defaulted to true (public).")
private boolean publicRole = true;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -60,6 +64,9 @@ public class CreateRoleCmd extends RoleCmd {
return roleId;
}
public boolean isPublicRole() {
return publicRole;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@ -81,10 +88,10 @@ public class CreateRoleCmd extends RoleCmd {
}
CallContext.current().setEventDetails("Role: " + getRoleName() + ", from role: " + getRoleId() + ", description: " + getRoleDescription());
role = roleService.createRole(getRoleName(), existingRole, getRoleDescription());
role = roleService.createRole(getRoleName(), existingRole, getRoleDescription(), isPublicRole());
} else {
CallContext.current().setEventDetails("Role: " + getRoleName() + ", type: " + getRoleType() + ", description: " + getRoleDescription());
role = roleService.createRole(getRoleName(), getRoleType(), getRoleDescription());
role = roleService.createRole(getRoleName(), getRoleType(), getRoleDescription(), isPublicRole());
}
if (role == null) {

View File

@ -64,6 +64,10 @@ public class ImportRoleCmd extends RoleCmd {
description = "Force create a role with the same name. This overrides the role type, description and rule permissions for the existing role. Default is false.")
private Boolean forced;
@Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Indicates whether the role will be visible to all users (public) or only to root admins (private)." +
" If this parameter is not specified during the creation of the role its value will be defaulted to true (public).")
private boolean publicRole = true;
@Inject
ApiServerService _apiServer;
@ -114,6 +118,10 @@ public class ImportRoleCmd extends RoleCmd {
return (forced != null) ? forced : false;
}
public boolean isPublicRole() {
return publicRole;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@ -130,7 +138,7 @@ public class ImportRoleCmd extends RoleCmd {
}
CallContext.current().setEventDetails("Role: " + getRoleName() + ", type: " + getRoleType() + ", description: " + getRoleDescription());
Role role = roleService.importRole(getRoleName(), getRoleType(), getRoleDescription(), getRules(), isForced());
Role role = roleService.importRole(getRoleName(), getRoleType(), getRoleDescription(), getRules(), isForced(), isPublicRole());
if (role == null) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to import role");
}

View File

@ -92,6 +92,7 @@ public class ListRolesCmd extends BaseListCmd {
roleResponse.setRoleType(role.getRoleType());
roleResponse.setDescription(role.getDescription());
roleResponse.setIsDefault(role.isDefault());
roleResponse.setPublicRole(role.isPublicRole());
roleResponse.setObjectName("role");
roleResponses.add(roleResponse);
}
@ -104,7 +105,7 @@ public class ListRolesCmd extends BaseListCmd {
public void execute() {
Pair<List<Role>, Integer> roles;
if (getId() != null && getId() > 0L) {
roles = new Pair<List<Role>, Integer>(Collections.singletonList(roleService.findRole(getId())), 1);
roles = new Pair<>(Collections.singletonList(roleService.findRole(getId(), true)), 1);
} else if (StringUtils.isNotBlank(getName()) || StringUtils.isNotBlank(getKeyword())) {
roles = roleService.findRolesByName(getName(), getKeyword(), getStartIndex(), getPageSizeVal());
} else if (getRoleType() != null) {

View File

@ -58,6 +58,7 @@ public abstract class RoleCmd extends BaseCmd {
response.setRoleName(role.getName());
response.setRoleType(role.getRoleType());
response.setDescription(role.getDescription());
response.setPublicRole(role.isPublicRole());
response.setResponseName(getCommandName());
response.setObjectName("role");
setResponseObject(response);

View File

@ -52,6 +52,9 @@ public class UpdateRoleCmd extends RoleCmd {
@Parameter(name = ApiConstants.DESCRIPTION, type = BaseCmd.CommandType.STRING, description = "The description of the role")
private String roleDescription;
@Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Indicates whether the role will be visible to all users (public) or only to root admins (private).")
private Boolean publicRole;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -64,6 +67,10 @@ public class UpdateRoleCmd extends RoleCmd {
return roleName;
}
public Boolean isPublicRole() {
return publicRole;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@ -80,7 +87,7 @@ public class UpdateRoleCmd extends RoleCmd {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role id provided");
}
CallContext.current().setEventDetails("Role: " + getRoleName() + ", type:" + getRoleType() + ", description: " + getRoleDescription());
role = roleService.updateRole(role, getRoleName(), getRoleType(), getRoleDescription());
role = roleService.updateRole(role, getRoleName(), getRoleType(), getRoleDescription(), isPublicRole());
setupResponse(role);
}

View File

@ -36,6 +36,11 @@ public class BaseRoleResponse extends BaseResponse {
@Param(description = "the description of the role")
private String roleDescription;
@SerializedName(ApiConstants.IS_PUBLIC)
@Param(description = "Indicates whether the role will be visible to all users (public) or only to root admins (private)." +
" If this parameter is not specified during the creation of the role its value will be defaulted to true (public).")
private boolean publicRole = true;
public void setId(String id) {
this.id = id;
}
@ -47,4 +52,8 @@ public class BaseRoleResponse extends BaseResponse {
public void setDescription(String description) {
this.roleDescription = description;
}
public void setPublicRole(boolean publicRole) {
this.publicRole = publicRole;
}
}

View File

@ -54,7 +54,7 @@ public class CreateRoleCmdTest {
when(role.getDescription()).thenReturn("User test");
when(role.getName()).thenReturn("testuser");
when(role.getRoleType()).thenReturn(RoleType.User);
when(roleService.createRole(createRoleCmd.getRoleName(), createRoleCmd.getRoleType(), createRoleCmd.getRoleDescription())).thenReturn(role);
when(roleService.createRole(createRoleCmd.getRoleName(), createRoleCmd.getRoleType(), createRoleCmd.getRoleDescription(), true)).thenReturn(role);
createRoleCmd.execute();
RoleResponse response = (RoleResponse) createRoleCmd.getResponseObject();
Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleName"), role.getName());
@ -71,7 +71,7 @@ public class CreateRoleCmdTest {
when(newRole.getDescription()).thenReturn("User test");
when(newRole.getName()).thenReturn("testuser");
when(newRole.getRoleType()).thenReturn(RoleType.User);
when(roleService.createRole(createRoleCmd.getRoleName(), role, createRoleCmd.getRoleDescription())).thenReturn(newRole);
when(roleService.createRole(createRoleCmd.getRoleName(), role, createRoleCmd.getRoleDescription(), true)).thenReturn(newRole);
createRoleCmd.execute();
RoleResponse response = (RoleResponse) createRoleCmd.getResponseObject();
Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleName"), newRole.getName());

View File

@ -93,7 +93,7 @@ public class ImportRoleCmdTest {
when(role.getDescription()).thenReturn("test user imported");
when(role.getName()).thenReturn("Test User");
when(role.getRoleType()).thenReturn(RoleType.User);
when(roleService.importRole(anyString(),any(), anyString(), any(), anyBoolean())).thenReturn(role);
when(roleService.importRole(anyString(), any(), anyString(), any(), anyBoolean(), anyBoolean())).thenReturn(role);
importRoleCmd.execute();
RoleResponse response = (RoleResponse) importRoleCmd.getResponseObject();

View File

@ -58,7 +58,7 @@ public class UpdateRoleCmdTest extends TestCase{
when(role.getDescription()).thenReturn("Default user");
when(role.getName()).thenReturn("User");
when(role.getRoleType()).thenReturn(RoleType.User);
when(roleService.updateRole(role,updateRoleCmd.getRoleName(),updateRoleCmd.getRoleType(),updateRoleCmd.getRoleDescription())).thenReturn(role);
when(roleService.updateRole(role, updateRoleCmd.getRoleName(), updateRoleCmd.getRoleType(), updateRoleCmd.getRoleDescription(), updateRoleCmd.isPublicRole())).thenReturn(role);
when(role.getId()).thenReturn(1L);
when(role.getDescription()).thenReturn("Description Initial");
when(role.getName()).thenReturn("User");

View File

@ -55,6 +55,9 @@ public class RoleVO implements Role {
@Column(name = "is_default")
private boolean isDefault = false;
@Column(name = "public_role")
private boolean publicRole = true;
@Column(name = GenericDao.REMOVED_COLUMN)
private Date removed;
@ -120,4 +123,12 @@ public class RoleVO implements Role {
public String toString() {
return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "name", "uuid", "roleType");
}
public boolean isPublicRole() {
return publicRole;
}
public void setPublicRole(boolean publicRole) {
this.publicRole = publicRole;
}
}

View File

@ -26,13 +26,15 @@ import org.apache.cloudstack.acl.RoleVO;
import java.util.List;
public interface RoleDao extends GenericDao<RoleVO, Long> {
List<RoleVO> findAllByName(String roleName);
List<RoleVO> findAllByName(String roleName, boolean showPrivateRole);
Pair<List<RoleVO>, Integer> findAllByName(final String roleName, String keyword, Long offset, Long limit);
Pair<List<RoleVO>, Integer> findAllByName(final String roleName, String keyword, Long offset, Long limit, boolean showPrivateRole);
List<RoleVO> findAllByRoleType(RoleType type);
List<RoleVO> findByName(String roleName);
RoleVO findByNameAndType(String roleName, RoleType type);
List<RoleVO> findAllByRoleType(RoleType type, boolean showPrivateRole);
List<RoleVO> findByName(String roleName, boolean showPrivateRole);
RoleVO findByNameAndType(String roleName, RoleType type, boolean showPrivateRole);
Pair<List<RoleVO>, Integer> findAllByRoleType(RoleType type, Long offset, Long limit);
Pair<List<RoleVO>, Integer> findAllByRoleType(RoleType type, Long offset, Long limit, boolean showPrivateRole);
Pair<List<RoleVO>, Integer> listAllRoles(Long startIndex, Long limit, boolean showPrivateRole);
}

View File

@ -35,32 +35,41 @@ public class RoleDaoImpl extends GenericDaoBase<RoleVO, Long> implements RoleDao
private final SearchBuilder<RoleVO> RoleByNameSearch;
private final SearchBuilder<RoleVO> RoleByTypeSearch;
private final SearchBuilder<RoleVO> RoleByNameAndTypeSearch;
private final SearchBuilder<RoleVO> RoleByIsPublicSearch;
public RoleDaoImpl() {
super();
RoleByNameSearch = createSearchBuilder();
RoleByNameSearch.and("roleName", RoleByNameSearch.entity().getName(), SearchCriteria.Op.LIKE);
RoleByNameSearch.and("isPublicRole", RoleByNameSearch.entity().isPublicRole(), SearchCriteria.Op.EQ);
RoleByNameSearch.done();
RoleByTypeSearch = createSearchBuilder();
RoleByTypeSearch.and("roleType", RoleByTypeSearch.entity().getRoleType(), SearchCriteria.Op.EQ);
RoleByTypeSearch.and("isPublicRole", RoleByTypeSearch.entity().isPublicRole(), SearchCriteria.Op.EQ);
RoleByTypeSearch.done();
RoleByNameAndTypeSearch = createSearchBuilder();
RoleByNameAndTypeSearch.and("roleName", RoleByNameAndTypeSearch.entity().getName(), SearchCriteria.Op.EQ);
RoleByNameAndTypeSearch.and("roleType", RoleByNameAndTypeSearch.entity().getRoleType(), SearchCriteria.Op.EQ);
RoleByNameAndTypeSearch.and("isPublicRole", RoleByNameAndTypeSearch.entity().isPublicRole(), SearchCriteria.Op.EQ);
RoleByNameAndTypeSearch.done();
RoleByIsPublicSearch = createSearchBuilder();
RoleByIsPublicSearch.and("isPublicRole", RoleByIsPublicSearch.entity().isPublicRole(), SearchCriteria.Op.EQ);
RoleByIsPublicSearch.done();
}
@Override
public List<RoleVO> findAllByName(final String roleName) {
return findAllByName(roleName, null, null, null).first();
public List<RoleVO> findAllByName(final String roleName, boolean showPrivateRole) {
return findAllByName(roleName, null, null, null, showPrivateRole).first();
}
@Override
public Pair<List<RoleVO>, Integer> findAllByName(final String roleName, String keyword, Long offset, Long limit) {
public Pair<List<RoleVO>, Integer> findAllByName(final String roleName, String keyword, Long offset, Long limit, boolean showPrivateRole) {
SearchCriteria<RoleVO> sc = RoleByNameSearch.create();
filterPrivateRolesIfNeeded(sc, showPrivateRole);
if (StringUtils.isNotEmpty(roleName)) {
sc.setParameters("roleName", roleName);
}
@ -72,28 +81,44 @@ public class RoleDaoImpl extends GenericDaoBase<RoleVO, Long> implements RoleDao
}
@Override
public List<RoleVO> findAllByRoleType(final RoleType type) {
return findAllByRoleType(type, null, null).first();
public List<RoleVO> findAllByRoleType(final RoleType type, boolean showPrivateRole) {
return findAllByRoleType(type, null, null, showPrivateRole).first();
}
public Pair<List<RoleVO>, Integer> findAllByRoleType(final RoleType type, Long offset, Long limit) {
public Pair<List<RoleVO>, Integer> findAllByRoleType(final RoleType type, Long offset, Long limit, boolean showPrivateRole) {
SearchCriteria<RoleVO> sc = RoleByTypeSearch.create();
filterPrivateRolesIfNeeded(sc, showPrivateRole);
sc.setParameters("roleType", type);
return searchAndCount(sc, new Filter(RoleVO.class, "id", true, offset, limit));
}
@Override
public List<RoleVO> findByName(String roleName) {
public List<RoleVO> findByName(String roleName, boolean showPrivateRole) {
SearchCriteria<RoleVO> sc = RoleByNameSearch.create();
filterPrivateRolesIfNeeded(sc, showPrivateRole);
sc.setParameters("roleName", roleName);
return listBy(sc);
}
@Override
public RoleVO findByNameAndType(String roleName, RoleType type) {
public RoleVO findByNameAndType(String roleName, RoleType type, boolean showPrivateRole) {
SearchCriteria<RoleVO> sc = RoleByNameAndTypeSearch.create();
filterPrivateRolesIfNeeded(sc, showPrivateRole);
sc.setParameters("roleName", roleName);
sc.setParameters("roleType", type);
return findOneBy(sc);
}
@Override
public Pair<List<RoleVO>, Integer> listAllRoles(Long startIndex, Long limit, boolean showPrivateRole) {
SearchCriteria<RoleVO> sc = RoleByIsPublicSearch.create();
filterPrivateRolesIfNeeded(sc, showPrivateRole);
return searchAndCount(sc, new Filter(RoleVO.class, "id", true, startIndex, limit));
}
public void filterPrivateRolesIfNeeded(SearchCriteria<RoleVO> sc, boolean showPrivateRole) {
if (!showPrivateRole) {
sc.setParameters("isPublicRole", true);
}
}
}

View File

@ -19,3 +19,5 @@
-- Schema upgrade from 4.18.0.0 to 4.18.1.0
--;
-- create_public_parameter_on_roles. #6960
ALTER TABLE `cloud`.`roles` ADD COLUMN `public_role` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Indicates whether the role will be visible to all users (public) or only to root admins (private). If this parameter is not specified during the creation of the role its value will be defaulted to true (public).';

View File

@ -56,7 +56,6 @@ import com.cloud.utils.ListUtils;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.component.PluggableService;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.db.TransactionStatus;
@ -95,7 +94,7 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
}
@Override
public Role findRole(Long id) {
public Role findRole(Long id, boolean removePrivateRoles) {
if (id == null || id < 1L) {
logger.trace(String.format("Role ID is invalid [%s]", id));
return null;
@ -105,14 +104,18 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
logger.trace(String.format("Role not found [id=%s]", id));
return null;
}
Account account = getCurrentAccount();
if (!accountManager.isRootAdmin(account.getId()) && RoleType.Admin == role.getRoleType()) {
logger.debug(String.format("Role [id=%s, name=%s] is of 'Admin' type and is only visible to 'Root admins'.", id, role.getName()));
if (!isCallerRootAdmin() && (RoleType.Admin == role.getRoleType() || (!role.isPublicRole() && removePrivateRoles))) {
logger.debug(String.format("Role [id=%s, name=%s] is either of 'Admin' type or is private and is only visible to 'Root admins'.", id, role.getName()));
return null;
}
return role;
}
@Override
public Role findRole(Long id) {
return findRole(id, false);
}
/**
* Simple call to {@link CallContext#current()} to retrieve the current calling account.
* This method facilitates unit testing, it avoids mocking static methods.
@ -140,7 +143,7 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
@Override
@ActionEvent(eventType = EventTypes.EVENT_ROLE_CREATE, eventDescription = "creating Role")
public Role createRole(final String name, final RoleType roleType, final String description) {
public Role createRole(final String name, final RoleType roleType, final String description, boolean publicRole) {
checkCallerAccess();
if (roleType == null || roleType == RoleType.Unknown) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role type provided");
@ -148,7 +151,9 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
return Transaction.execute(new TransactionCallback<RoleVO>() {
@Override
public RoleVO doInTransaction(TransactionStatus status) {
RoleVO role = roleDao.persist(new RoleVO(name, roleType, description));
RoleVO role = new RoleVO(name, roleType, description);
role.setPublicRole(publicRole);
role = roleDao.persist(role);
CallContext.current().putContextParameter(Role.class, role.getUuid());
return role;
}
@ -157,12 +162,14 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
@Override
@ActionEvent(eventType = EventTypes.EVENT_ROLE_CREATE, eventDescription = "creating role by cloning another role")
public Role createRole(String name, Role role, String description) {
public Role createRole(String name, Role role, String description, boolean publicRole) {
checkCallerAccess();
return Transaction.execute(new TransactionCallback<RoleVO>() {
@Override
public RoleVO doInTransaction(TransactionStatus status) {
RoleVO newRoleVO = roleDao.persist(new RoleVO(name, role.getRoleType(), description));
RoleVO newRole = new RoleVO(name, role.getRoleType(), description);
newRole.setPublicRole(publicRole);
RoleVO newRoleVO = roleDao.persist(newRole);
if (newRoleVO == null) {
throw new CloudRuntimeException("Unable to create the role: " + name + ", failed to persist in DB");
}
@ -181,7 +188,7 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
@Override
@ActionEvent(eventType = EventTypes.EVENT_ROLE_IMPORT, eventDescription = "importing Role")
public Role importRole(String name, RoleType type, String description, List<Map<String, Object>> rules, boolean forced) {
public Role importRole(String name, RoleType type, String description, List<Map<String, Object>> rules, boolean forced, boolean isPublicRole) {
checkCallerAccess();
if (StringUtils.isEmpty(name)) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role name provided");
@ -190,7 +197,7 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role type provided");
}
List<RoleVO> existingRoles = roleDao.findByName(name);
List<RoleVO> existingRoles = roleDao.findByName(name, isCallerRootAdmin());
if (CollectionUtils.isNotEmpty(existingRoles) && !forced) {
throw new CloudRuntimeException("Role already exists");
}
@ -199,7 +206,7 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
@Override
public RoleVO doInTransaction(TransactionStatus status) {
RoleVO newRole = null;
RoleVO existingRole = roleDao.findByNameAndType(name, type);
RoleVO existingRole = roleDao.findByNameAndType(name, type, isCallerRootAdmin());
if (existingRole != null) {
if (existingRole.isDefault()) {
throw new CloudRuntimeException("Failed to import the role: " + name + ", default role cannot be overriden");
@ -216,11 +223,14 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
existingRole.setName(name);
existingRole.setRoleType(type);
existingRole.setDescription(description);
existingRole.setPublicRole(isPublicRole);
roleDao.update(existingRole.getId(), existingRole);
newRole = existingRole;
} else {
newRole = roleDao.persist(new RoleVO(name, type, description));
RoleVO role = new RoleVO(name, type, description);
role.setPublicRole(isPublicRole);
newRole = roleDao.persist(role);
}
if (newRole == null) {
@ -243,16 +253,23 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
@Override
@ActionEvent(eventType = EventTypes.EVENT_ROLE_UPDATE, eventDescription = "updating Role")
public Role updateRole(final Role role, final String name, final RoleType roleType, final String description) {
public Role updateRole(final Role role, final String name, final RoleType roleType, final String description, Boolean publicRole) {
checkCallerAccess();
if (role.isDefault()) {
throw new PermissionDeniedException("Default roles cannot be updated");
}
if (roleType != null && roleType == RoleType.Unknown) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unknown is not a valid role type");
}
RoleVO roleVO = (RoleVO)role;
if (role.isDefault()) {
if (publicRole == null || roleType != null || !StringUtils.isAllEmpty(name, description)) {
throw new PermissionDeniedException("Default roles cannot be updated (with the exception of making it private/public).");
}
roleVO.setPublicRole(publicRole);
roleDao.update(role.getId(), roleVO);
return role;
}
if (StringUtils.isNotEmpty(name)) {
roleVO.setName(name);
}
@ -268,6 +285,10 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
roleVO.setDescription(description);
}
if (publicRole == null) {
publicRole = role.isPublicRole();
}
roleVO.setPublicRole(publicRole);
roleDao.update(role.getId(), roleVO);
return role;
}
@ -363,7 +384,7 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
@Override
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);
Pair<List<RoleVO>, Integer> data = roleDao.findAllByName(name, keyword, startIndex, limit, isCallerRootAdmin());
int removed = removeRootAdminRolesIfNeeded(data.first());
return new Pair<List<Role>,Integer>(ListUtils.toListOfInterface(data.first()), Integer.valueOf(data.second() - removed));
}
@ -375,8 +396,7 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
* The actual removal is executed via {@link #removeRootAdminRoles(List)}. Therefore, if the method is called by a 'root admin', we do nothing here.
*/
protected int removeRootAdminRolesIfNeeded(List<? extends Role> roles) {
Account account = getCurrentAccount();
if (!accountManager.isRootAdmin(account.getId())) {
if (!isCallerRootAdmin()) {
return removeRootAdminRoles(roles);
}
return 0;
@ -408,10 +428,10 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
@Override
public Pair<List<Role>, Integer> findRolesByType(RoleType roleType, Long startIndex, Long limit) {
if (roleType == null || RoleType.Admin == roleType && !accountManager.isRootAdmin(getCurrentAccount().getId())) {
if (roleType == null || RoleType.Admin == roleType && !isCallerRootAdmin()) {
return new Pair<List<Role>, Integer>(Collections.emptyList(), 0);
}
Pair<List<RoleVO>, Integer> data = roleDao.findAllByRoleType(roleType, startIndex, limit);
Pair<List<RoleVO>, Integer> data = roleDao.findAllByRoleType(roleType, startIndex, limit, isCallerRootAdmin());
return new Pair<List<Role>,Integer>(ListUtils.toListOfInterface(data.first()), Integer.valueOf(data.second()));
}
@ -424,8 +444,7 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
@Override
public Pair<List<Role>, Integer> listRoles(Long startIndex, Long limit) {
Pair<List<RoleVO>, Integer> data = roleDao.searchAndCount(null,
new Filter(RoleVO.class, "id", Boolean.TRUE, startIndex, limit));
Pair<List<RoleVO>, Integer> data = roleDao.listAllRoles(startIndex, limit, isCallerRootAdmin());
int removed = removeRootAdminRolesIfNeeded(data.first());
return new Pair<List<Role>,Integer>(ListUtils.toListOfInterface(data.first()), Integer.valueOf(data.second() - removed));
}
@ -439,6 +458,10 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
return Collections.emptyList();
}
private boolean isCallerRootAdmin() {
return accountManager.isRootAdmin(getCurrentAccount().getId());
}
@Override
public Permission getRolePermission(String permission) {
if (StringUtils.isEmpty(permission)) {

View File

@ -166,7 +166,7 @@ public class RoleManagerImplTest {
String roleName = "roleName";
List<Role> roles = new ArrayList<>();
Pair<ArrayList<RoleVO>, Integer> toBeReturned = new Pair(roles, 0);
Mockito.doReturn(toBeReturned).when(roleDaoMock).findAllByName(roleName, null, null, null);
Mockito.doReturn(toBeReturned).when(roleDaoMock).findAllByName(roleName, null, null, null, false);
roleManagerImpl.findRolesByName(roleName);
Mockito.verify(roleManagerImpl).removeRootAdminRolesIfNeeded(roles);
@ -239,7 +239,7 @@ public class RoleManagerImplTest {
Assert.assertEquals(0, returnedRoles.size());
Mockito.verify(accountManagerMock, Mockito.times(1)).isRootAdmin(Mockito.anyLong());
Mockito.verify(roleDaoMock, Mockito.times(0)).findAllByRoleType(Mockito.any(RoleType.class));
Mockito.verify(roleDaoMock, Mockito.times(0)).findAllByRoleType(Mockito.any(RoleType.class), Mockito.anyBoolean());
}
@Test
@ -250,11 +250,11 @@ public class RoleManagerImplTest {
List<Role> roles = new ArrayList<>();
roles.add(Mockito.mock(Role.class));
Pair<ArrayList<RoleVO>, Integer> toBeReturned = new Pair(roles, 1);
Mockito.doReturn(toBeReturned).when(roleDaoMock).findAllByRoleType(RoleType.Admin, null, null);
Mockito.doReturn(toBeReturned).when(roleDaoMock).findAllByRoleType(RoleType.Admin, null, null, true);
List<Role> returnedRoles = roleManagerImpl.findRolesByType(RoleType.Admin);
Assert.assertEquals(1, returnedRoles.size());
Mockito.verify(accountManagerMock, Mockito.times(1)).isRootAdmin(Mockito.anyLong());
Mockito.verify(accountManagerMock, Mockito.times(2)).isRootAdmin(Mockito.anyLong());
}
@Test
@ -265,11 +265,11 @@ public class RoleManagerImplTest {
List<Role> roles = new ArrayList<>();
roles.add(Mockito.mock(Role.class));
Pair<ArrayList<RoleVO>, Integer> toBeReturned = new Pair(roles, 1);
Mockito.doReturn(toBeReturned).when(roleDaoMock).findAllByRoleType(RoleType.User, null, null);
Mockito.doReturn(toBeReturned).when(roleDaoMock).findAllByRoleType(RoleType.User, null, null, true);
List<Role> returnedRoles = roleManagerImpl.findRolesByType(RoleType.User);
Assert.assertEquals(1, returnedRoles.size());
Mockito.verify(accountManagerMock, Mockito.times(0)).isRootAdmin(Mockito.anyLong());
Mockito.verify(accountManagerMock, Mockito.times(1)).isRootAdmin(Mockito.anyLong());
}
@Test

View File

@ -0,0 +1,275 @@
# 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.
from marvin.cloudstackAPI import *
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.lib.base import Account, Role, User
from marvin.lib.utils import cleanup_resources
from nose.plugins.attrib import attr
import random
class TestData(object):
"""Test data object that is required to create resources
"""
def __init__(self):
self.testdata = {
"accountadmin": {
"email": "mtu@test.cloud",
"firstname": "Marvin",
"lastname": "TestAdminAccount",
"username": "TestAdminAccount",
"password": "password"
},
"accountdomainadmin": {
"email": "mtu@test.cloud",
"firstname": "Marvin",
"lastname": "TestDomainAdminAccount",
"username": "TestDomainAdminAccount",
"password": "password"
},
"accountroleuser": {
"email": "mtu@test.cloud",
"firstname": "Marvin",
"lastname": "TestUserAccount",
"username": "TestUserAccount",
"password": "password"
},
"roleadmin": {
"name": "MarvinFake Admin Role ",
"type": "Admin",
"description": "Fake Admin Role created by Marvin test"
},
"roleuser": {
"name": "MarvinFake User Role ",
"type": "User",
"description": "Fake User Role created by Marvin test",
"ispublic": False
},
"publicrole": {
"name": "MarvinFake Public Role ",
"type": "User",
"description": "Fake Public Role created by Marvin test"
},
"importrole": {
"name": "MarvinFake Import Role ",
"type": "User",
"description": "Fake Import User Role created by Marvin test",
"ispublic": True,
"rules": [{"rule":"list*", "permission":"allow","description":"Listing apis"},
{"rule":"get*", "permission":"allow","description":"Get apis"},
{"rule":"update*", "permission":"deny","description":"Update apis"}]
},
"roledomainadmin": {
"name": "MarvinFake DomainAdmin Role ",
"type": "DomainAdmin",
"description": "Fake Domain-Admin Role created by Marvin test",
"ispublic": False
},
"apiConfig": {
"listApis": "allow",
"listAccounts": "allow",
"listClusters": "deny",
"*VM*": "allow",
"*Host*": "deny"
}
}
class TestPrivateRoles(cloudstackTestCase):
"""Tests Visibility of private and public roles
"""
def setUp(self):
self.apiclient = self.testClient.getApiClient()
self.testdata = TestData().testdata
self.testdata["roleadmin"]["name"] += self.getRandomString()
self.testdata["roledomainadmin"]["name"] += self.getRandomString()
self.testdata["roleuser"]["name"] += self.getRandomString()
self.cleanup = []
self.role_admin = Role.create(
self.apiclient,
self.testdata["roleadmin"]
)
self.cleanup.append(self.role_admin)
self.role_domain_admin = Role.create(
self.apiclient,
self.testdata["roledomainadmin"]
)
self.cleanup.append(self.role_domain_admin)
self.private_role = Role.create(
self.apiclient,
self.testdata["roleuser"]
)
self.cleanup.append(self.private_role)
self.account_admin = Account.create(
self.apiclient,
self.testdata["accountadmin"],
roleid=self.role_admin.id
)
self.cleanup.append(self.account_admin)
self.account_domain_admin = Account.create(
self.apiclient,
self.testdata["accountdomainadmin"],
roleid=self.role_domain_admin.id
)
self.cleanup.append(self.account_domain_admin)
self.admin_apiclient = self.testClient.getUserApiClient(
UserName=self.account_admin.name,
DomainName='ROOT',
type=1
)
self.domain_admin_apiclient = self.testClient.getUserApiClient(
UserName=self.account_domain_admin.name,
DomainName='ROOT',
type=2
)
def tearDown(self):
super(TestPrivateRoles, self).tearDown()
def getRandomString(self):
return "".join(random.choice("abcdefghijklmnopqrstuvwxyz0123456789") for _ in range(10))
def asserts_visibility_of_private_role(self, role_id):
list_roles_domain_admin = Role.list(self.domain_admin_apiclient, id=role_id)
self.assertEqual(
list_roles_domain_admin,
None,
"Domain Admins should not be able to list private roles"
)
list_roles_admin = Role.list(self.admin_apiclient, id=role_id)
self.assertNotEqual(
list_roles_admin,
None,
"Admins should be able to list private roles"
)
def asserts_visibility_of_public_role(self, role_id):
list_roles_domain_admin = Role.list(self.domain_admin_apiclient, id=role_id)
self.assertNotEqual(
list_roles_domain_admin,
None,
"Domain Admins should be able to list public roles"
)
list_roles_admin = Role.list(self.admin_apiclient, id=role_id)
self.assertNotEqual(
list_roles_admin,
None,
"Admins should be able to list public roles"
)
@attr(tags=['simulator', 'basic'], required_hardware=False)
def test_create_role(self):
"""
1. Create a private role
2. Create a public role
3. Verify whether their visibility is as expected
"""
self.testdata["roleuser"]["name"] += self.getRandomString()
self.testdata["publicrole"]["name"] += self.getRandomString()
private_role = Role.create(
self.apiclient,
self.testdata["roleuser"]
)
self.cleanup.append(self.private_role)
public_role = Role.create(
self.apiclient,
self.testdata["publicrole"]
)
self.cleanup.append(self.public_role)
self.asserts_visibility_of_private_role(private_role.id)
self.asserts_visibility_of_public_role(public_role.id)
@attr(tags=['simulator', 'basic'], required_hardware=False)
def test_update_role(self):
"""
1. Create a public role
2. Check if its visibility is public
3. Update it to make it private
4. Verify if its visibility is private
"""
self.testdata["publicrole"]["name"] += self.getRandomString()
role = Role.create(
self.apiclient,
self.testdata["publicrole"]
)
self.cleanup.append(role)
self.asserts_visibility_of_public_role(role.id)
role.update(self.apiclient, id=role.id, ispublic=False)
self.asserts_visibility_of_private_role(role.id)
@attr(tags=['simulator', 'basic'], required_hardware=False)
def test_import_role(self):
"""
1. Import a public role
2. Import a private role
3. Verify their visibility
"""
self.testdata["importrole"]["name"] += self.getRandomString()
imported_public_role = Role.importRole(
self.apiclient,
self.testdata["importrole"]
)
self.cleanup.append(imported_public_role)
self.testdata["importrole"]["name"] += self.getRandomString()
self.testdata["importrole"]["ispublic"] = False
imported_private_role = Role.importRole(
self.apiclient,
self.testdata["importrole"]
)
self.cleanup.append(imported_private_role)
self.asserts_visibility_of_public_role(imported_public_role.id)
self.asserts_visibility_of_private_role(imported_private_role.id)
@attr(tags=['simulator', 'basic'], required_hardware=False)
def test_login_private_role(self):
"""
1. Crate a User account with a private role
2. Login with the created account
3. Verify that the login was successful
"""
account_private_role = Account.create(
self.apiclient,
self.testdata["accountroleuser"],
roleid=self.private_role.id
)
self.cleanup.append(account_private_role)
response = User.login(
self.apiclient,
username=account_private_role.name,
password=self.testdata["accountroleuser"]["password"]
)
self.assertNotEqual(
response.sessionkey,
None,
"Accounts using private roles should be able to login."
)

View File

@ -108,6 +108,8 @@ class Role:
cmd.roleid = services["roleid"]
if "description" in services:
cmd.description = services["description"]
if "ispublic" in services:
cmd.ispublic = services["ispublic"]
return Role(apiclient.createRole(cmd).__dict__)
@ -122,6 +124,8 @@ class Role:
cmd.description = services["description"]
if "forced" in services:
cmd.type = services["forced"]
if "ispublic" in services:
cmd.ispublic = services["ispublic"]
return Role(apiclient.importRole(cmd).__dict__)