This is an automated email from the ASF dual-hosted git repository.
dahn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new 8b5bfb145e3 create parameter to determine whether roles are public or
private (#6960)
8b5bfb145e3 is described below
commit 8b5bfb145e335209e261056a691ffeb7372ca0e1
Author: GaOrtiga <[email protected]>
AuthorDate: Mon May 1 10:26:10 2023 -0300
create parameter to determine whether roles are public or private (#6960)
Co-authored-by: Gabriel Ortiga Fernandes <[email protected]>
Co-authored-by: dahn <[email protected]>
---
.../main/java/org/apache/cloudstack/acl/Role.java | 1 +
.../org/apache/cloudstack/acl/RoleService.java | 10 +-
.../api/command/admin/acl/CreateRoleCmd.java | 11 +-
.../api/command/admin/acl/ImportRoleCmd.java | 10 +-
.../api/command/admin/acl/ListRolesCmd.java | 3 +-
.../cloudstack/api/command/admin/acl/RoleCmd.java | 1 +
.../api/command/admin/acl/UpdateRoleCmd.java | 9 +-
.../cloudstack/api/response/BaseRoleResponse.java | 9 +
.../api/command/test/CreateRoleCmdTest.java | 4 +-
.../api/command/test/ImportRoleCmdTest.java | 2 +-
.../api/command/test/UpdateRoleCmdTest.java | 2 +-
.../java/org/apache/cloudstack/acl/RoleVO.java | 11 +
.../org/apache/cloudstack/acl/dao/RoleDao.java | 14 +-
.../org/apache/cloudstack/acl/dao/RoleDaoImpl.java | 41 ++-
.../resources/META-INF/db/schema-41800to41810.sql | 2 +
.../org/apache/cloudstack/acl/RoleManagerImpl.java | 71 ++++--
.../apache/cloudstack/acl/RoleManagerImplTest.java | 12 +-
test/integration/smoke/test_private_roles.py | 275 +++++++++++++++++++++
tools/marvin/marvin/lib/base.py | 4 +
19 files changed, 435 insertions(+), 57 deletions(-)
diff --git a/api/src/main/java/org/apache/cloudstack/acl/Role.java
b/api/src/main/java/org/apache/cloudstack/acl/Role.java
index c25d7a99867..5e5ffd583d8 100644
--- a/api/src/main/java/org/apache/cloudstack/acl/Role.java
+++ b/api/src/main/java/org/apache/cloudstack/acl/Role.java
@@ -23,4 +23,5 @@ import org.apache.cloudstack.api.InternalIdentity;
public interface Role extends RoleEntity, InternalIdentity, Identity {
RoleType getRoleType();
boolean isDefault();
+ boolean isPublicRole();
}
diff --git a/api/src/main/java/org/apache/cloudstack/acl/RoleService.java
b/api/src/main/java/org/apache/cloudstack/acl/RoleService.java
index 578c13ef6fd..9becce643d4 100644
--- a/api/src/main/java/org/apache/cloudstack/acl/RoleService.java
+++ b/api/src/main/java/org/apache/cloudstack/acl/RoleService.java
@@ -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);
diff --git
a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java
b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java
index 2b8800319f6..8a469df1f1f 100644
---
a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java
+++
b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java
@@ -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) {
diff --git
a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ImportRoleCmd.java
b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ImportRoleCmd.java
index 4f3e9d15af7..058650cf42c 100644
---
a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ImportRoleCmd.java
+++
b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ImportRoleCmd.java
@@ -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");
}
diff --git
a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java
b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java
index b55dc80e705..fef2b27eaa5 100644
---
a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java
+++
b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java
@@ -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) {
diff --git
a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java
b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java
index e652918c4ca..4c317d06b13 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java
@@ -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);
diff --git
a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java
b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java
index 227aaf54f7f..7d002cd889b 100644
---
a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java
+++
b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java
@@ -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);
}
diff --git
a/api/src/main/java/org/apache/cloudstack/api/response/BaseRoleResponse.java
b/api/src/main/java/org/apache/cloudstack/api/response/BaseRoleResponse.java
index 339d092c1f2..b1bba905bc6 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/BaseRoleResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/BaseRoleResponse.java
@@ -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;
+ }
}
diff --git
a/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java
b/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java
index a910de789a5..4b9d4fd8974 100644
---
a/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java
+++
b/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java
@@ -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());
diff --git
a/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java
b/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java
index 8de01489dab..6299c1ed8e2 100644
---
a/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java
+++
b/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java
@@ -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();
diff --git
a/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateRoleCmdTest.java
b/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateRoleCmdTest.java
index d298defb603..84b91525742 100644
---
a/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateRoleCmdTest.java
+++
b/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateRoleCmdTest.java
@@ -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");
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java
b/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java
index f5a0cebc75f..d4647255fc6 100644
--- a/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java
@@ -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;
+ }
}
diff --git
a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java
b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java
index 36833d5e790..a776f7b6fe6 100644
--- a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java
@@ -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);
}
diff --git
a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java
b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java
index b4938a1e833..06d3108076a 100644
--- a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java
@@ -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);
+ }
+ }
}
diff --git
a/engine/schema/src/main/resources/META-INF/db/schema-41800to41810.sql
b/engine/schema/src/main/resources/META-INF/db/schema-41800to41810.sql
index ae4be2d3122..e595f007a6d 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41800to41810.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41800to41810.sql
@@ -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).';
diff --git
a/server/src/main/java/org/apache/cloudstack/acl/RoleManagerImpl.java
b/server/src/main/java/org/apache/cloudstack/acl/RoleManagerImpl.java
index f2f4a6fe06a..ff97d7ecf6f 100644
--- a/server/src/main/java/org/apache/cloudstack/acl/RoleManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/acl/RoleManagerImpl.java
@@ -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)) {
diff --git
a/server/src/test/java/org/apache/cloudstack/acl/RoleManagerImplTest.java
b/server/src/test/java/org/apache/cloudstack/acl/RoleManagerImplTest.java
index 049bc17e0b6..9f0d40a6c6b 100644
--- a/server/src/test/java/org/apache/cloudstack/acl/RoleManagerImplTest.java
+++ b/server/src/test/java/org/apache/cloudstack/acl/RoleManagerImplTest.java
@@ -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
diff --git a/test/integration/smoke/test_private_roles.py
b/test/integration/smoke/test_private_roles.py
new file mode 100644
index 00000000000..32d48833348
--- /dev/null
+++ b/test/integration/smoke/test_private_roles.py
@@ -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": "[email protected]",
+ "firstname": "Marvin",
+ "lastname": "TestAdminAccount",
+ "username": "TestAdminAccount",
+ "password": "password"
+ },
+ "accountdomainadmin": {
+ "email": "[email protected]",
+ "firstname": "Marvin",
+ "lastname": "TestDomainAdminAccount",
+ "username": "TestDomainAdminAccount",
+ "password": "password"
+ },
+ "accountroleuser": {
+ "email": "[email protected]",
+ "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."
+ )
diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py
index 9d28d5a3f2a..46015f7d7b7 100755
--- a/tools/marvin/marvin/lib/base.py
+++ b/tools/marvin/marvin/lib/base.py
@@ -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__)