Skip to content

Commit d32ace6

Browse files
authored
New Feature: Enable/Disable Roles (#9549)
* New feature: Enable/Disable Roles * Fixes * Fix unit tests
1 parent d7ca05e commit d32ace6

20 files changed

Lines changed: 314 additions & 27 deletions

File tree

api/src/main/java/com/cloud/event/EventTypes.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ public class EventTypes {
242242
public static final String EVENT_ROLE_UPDATE = "ROLE.UPDATE";
243243
public static final String EVENT_ROLE_DELETE = "ROLE.DELETE";
244244
public static final String EVENT_ROLE_IMPORT = "ROLE.IMPORT";
245+
public static final String EVENT_ROLE_ENABLE = "ROLE.ENABLE";
246+
public static final String EVENT_ROLE_DISABLE = "ROLE.DISABLE";
245247
public static final String EVENT_ROLE_PERMISSION_CREATE = "ROLE.PERMISSION.CREATE";
246248
public static final String EVENT_ROLE_PERMISSION_UPDATE = "ROLE.PERMISSION.UPDATE";
247249
public static final String EVENT_ROLE_PERMISSION_DELETE = "ROLE.PERMISSION.DELETE";
@@ -842,6 +844,8 @@ public class EventTypes {
842844
entityEventDetails.put(EVENT_ROLE_UPDATE, Role.class);
843845
entityEventDetails.put(EVENT_ROLE_DELETE, Role.class);
844846
entityEventDetails.put(EVENT_ROLE_IMPORT, Role.class);
847+
entityEventDetails.put(EVENT_ROLE_ENABLE, Role.class);
848+
entityEventDetails.put(EVENT_ROLE_DISABLE, Role.class);
845849
entityEventDetails.put(EVENT_ROLE_PERMISSION_CREATE, RolePermission.class);
846850
entityEventDetails.put(EVENT_ROLE_PERMISSION_UPDATE, RolePermission.class);
847851
entityEventDetails.put(EVENT_ROLE_PERMISSION_DELETE, RolePermission.class);

api/src/main/java/org/apache/cloudstack/acl/Role.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,18 @@
2121
import org.apache.cloudstack.api.InternalIdentity;
2222

2323
public interface Role extends RoleEntity, InternalIdentity, Identity {
24+
25+
enum State {
26+
ENABLED, DISABLED;
27+
28+
@Override
29+
public String toString(){
30+
return super.toString().toLowerCase();
31+
}
32+
}
33+
2434
RoleType getRoleType();
2535
boolean isDefault();
2636
boolean isPublicRole();
37+
State getState();
2738
}

api/src/main/java/org/apache/cloudstack/acl/RoleService.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ public interface RoleService {
5454

5555
boolean deleteRole(Role role);
5656

57+
boolean enableRole(Role role);
58+
59+
boolean disableRole(Role role);
60+
5761
RolePermission findRolePermission(Long id);
5862

5963
RolePermission findRolePermissionByRoleIdAndRule(Long roleId, String rule);
@@ -76,22 +80,22 @@ public interface RoleService {
7680
*/
7781
List<Role> listRoles();
7882

79-
Pair<List<Role>, Integer> listRoles(Long startIndex, Long limit);
83+
Pair<List<Role>, Integer> listRoles(String state, Long startIndex, Long limit);
8084

8185
/**
8286
* Find all roles that have the giving {@link String} as part of their name.
8387
* If the user calling the method is not a 'root admin', roles of type {@link RoleType#Admin} wil lbe removed of the returned list.
8488
*/
8589
List<Role> findRolesByName(String name);
8690

87-
Pair<List<Role>, Integer> findRolesByName(String name, String keyword, Long startIndex, Long limit);
91+
Pair<List<Role>, Integer> findRolesByName(String name, String keyword, String state, Long startIndex, Long limit);
8892

8993
/**
9094
* Find all roles by {@link RoleType}. If the role type is {@link RoleType#Admin}, the calling account must be a root admin, otherwise we return an empty list.
9195
*/
9296
List<Role> findRolesByType(RoleType roleType);
9397

94-
Pair<List<Role>, Integer> findRolesByType(RoleType roleType, Long startIndex, Long limit);
98+
Pair<List<Role>, Integer> findRolesByType(RoleType roleType, String state, Long startIndex, Long limit);
9599

96100
List<RolePermission> findAllPermissionsBy(Long roleId);
97101

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.api.command.admin.acl;
18+
19+
import com.cloud.exception.ConcurrentOperationException;
20+
import com.cloud.exception.InsufficientCapacityException;
21+
import com.cloud.exception.NetworkRuleConflictException;
22+
import com.cloud.exception.ResourceAllocationException;
23+
import com.cloud.exception.ResourceUnavailableException;
24+
import com.cloud.user.Account;
25+
import org.apache.cloudstack.acl.Role;
26+
import org.apache.cloudstack.acl.RoleType;
27+
import org.apache.cloudstack.api.APICommand;
28+
import org.apache.cloudstack.api.ApiArgValidator;
29+
import org.apache.cloudstack.api.ApiConstants;
30+
import org.apache.cloudstack.api.ApiErrorCode;
31+
import org.apache.cloudstack.api.BaseCmd;
32+
import org.apache.cloudstack.api.Parameter;
33+
import org.apache.cloudstack.api.ServerApiException;
34+
import org.apache.cloudstack.api.response.RoleResponse;
35+
import org.apache.cloudstack.api.response.SuccessResponse;
36+
import org.apache.cloudstack.context.CallContext;
37+
38+
@APICommand(name = "disableRole", description = "Disables a role", responseObject = SuccessResponse.class,
39+
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
40+
since = "4.20.0",
41+
authorized = {RoleType.Admin})
42+
public class DisableRoleCmd extends BaseCmd {
43+
44+
@Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, required = true, entityType = RoleResponse.class,
45+
description = "ID of the role", validations = {ApiArgValidator.PositiveNumber})
46+
private Long roleId;
47+
48+
public Long getRoleId() {
49+
return roleId;
50+
}
51+
52+
@Override
53+
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
54+
Role role = roleService.findRole(getRoleId());
55+
if (role == null) {
56+
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Cannot find the role with provided id");
57+
}
58+
CallContext.current().setEventDetails("Role id: " + role.getId());
59+
boolean result = roleService.disableRole(role);
60+
SuccessResponse response = new SuccessResponse(getCommandName());
61+
response.setSuccess(result);
62+
setResponseObject(response);
63+
}
64+
65+
@Override
66+
public long getEntityOwnerId() {
67+
return Account.ACCOUNT_ID_SYSTEM;
68+
}
69+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.api.command.admin.acl;
18+
19+
import com.cloud.exception.ConcurrentOperationException;
20+
import com.cloud.exception.InsufficientCapacityException;
21+
import com.cloud.exception.NetworkRuleConflictException;
22+
import com.cloud.exception.ResourceAllocationException;
23+
import com.cloud.exception.ResourceUnavailableException;
24+
import com.cloud.user.Account;
25+
import org.apache.cloudstack.acl.Role;
26+
import org.apache.cloudstack.acl.RoleType;
27+
import org.apache.cloudstack.api.APICommand;
28+
import org.apache.cloudstack.api.ApiArgValidator;
29+
import org.apache.cloudstack.api.ApiConstants;
30+
import org.apache.cloudstack.api.ApiErrorCode;
31+
import org.apache.cloudstack.api.BaseCmd;
32+
import org.apache.cloudstack.api.Parameter;
33+
import org.apache.cloudstack.api.ServerApiException;
34+
import org.apache.cloudstack.api.response.RoleResponse;
35+
import org.apache.cloudstack.api.response.SuccessResponse;
36+
import org.apache.cloudstack.context.CallContext;
37+
38+
@APICommand(name = "enableRole", description = "Enables a role", responseObject = SuccessResponse.class,
39+
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
40+
since = "4.20.0",
41+
authorized = {RoleType.Admin})
42+
public class EnableRoleCmd extends BaseCmd {
43+
44+
@Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, required = true, entityType = RoleResponse.class,
45+
description = "ID of the role", validations = {ApiArgValidator.PositiveNumber})
46+
private Long roleId;
47+
48+
public Long getRoleId() {
49+
return roleId;
50+
}
51+
52+
@Override
53+
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
54+
Role role = roleService.findRole(getRoleId());
55+
if (role == null) {
56+
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Cannot find the role with provided id");
57+
}
58+
CallContext.current().setEventDetails("Role id: " + role.getId());
59+
boolean result = roleService.enableRole(role);
60+
SuccessResponse response = new SuccessResponse(getCommandName());
61+
response.setSuccess(result);
62+
setResponseObject(response);
63+
}
64+
65+
@Override
66+
public long getEntityOwnerId() {
67+
return Account.ACCOUNT_ID_SYSTEM;
68+
}
69+
}

api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Collections;
2222
import java.util.List;
2323

24+
import com.cloud.exception.InvalidParameterValueException;
2425
import org.apache.cloudstack.acl.Role;
2526
import org.apache.cloudstack.acl.RoleType;
2627
import org.apache.cloudstack.api.APICommand;
@@ -51,6 +52,9 @@ public class ListRolesCmd extends BaseListCmd {
5152
@Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "List role by role type, valid options are: Admin, ResourceAdmin, DomainAdmin, User.")
5253
private String roleType;
5354

55+
@Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "List role by role type status, valid options are: enabled, disabled")
56+
private String roleState;
57+
5458
/////////////////////////////////////////////////////
5559
/////////////////// Accessors ///////////////////////
5660
/////////////////////////////////////////////////////
@@ -70,6 +74,17 @@ public RoleType getRoleType() {
7074
return null;
7175
}
7276

77+
public Role.State getRoleState() {
78+
if (roleState == null) {
79+
return null;
80+
}
81+
try {
82+
return Role.State.valueOf(roleState.toUpperCase());
83+
} catch (IllegalArgumentException e) {
84+
throw new InvalidParameterValueException("Unrecognized role state value");
85+
}
86+
}
87+
7388
/////////////////////////////////////////////////////
7489
/////////////// API Implementation///////////////////
7590
/////////////////////////////////////////////////////
@@ -93,6 +108,7 @@ private void setupResponse(final Pair<List<Role>, Integer> roles) {
93108
roleResponse.setDescription(role.getDescription());
94109
roleResponse.setIsDefault(role.isDefault());
95110
roleResponse.setPublicRole(role.isPublicRole());
111+
roleResponse.setState(role.getState().toString());
96112
roleResponse.setObjectName("role");
97113
roleResponses.add(roleResponse);
98114
}
@@ -104,14 +120,16 @@ private void setupResponse(final Pair<List<Role>, Integer> roles) {
104120
@Override
105121
public void execute() {
106122
Pair<List<Role>, Integer> roles;
123+
Role.State state = getRoleState();
124+
String roleStateStr = state != null ? state.toString() : null;
107125
if (getId() != null && getId() > 0L) {
108126
roles = new Pair<>(Collections.singletonList(roleService.findRole(getId(), true)), 1);
109127
} else if (StringUtils.isNotBlank(getName()) || StringUtils.isNotBlank(getKeyword())) {
110-
roles = roleService.findRolesByName(getName(), getKeyword(), getStartIndex(), getPageSizeVal());
128+
roles = roleService.findRolesByName(getName(), getKeyword(), roleStateStr, getStartIndex(), getPageSizeVal());
111129
} else if (getRoleType() != null) {
112-
roles = roleService.findRolesByType(getRoleType(), getStartIndex(), getPageSizeVal());
130+
roles = roleService.findRolesByType(getRoleType(), roleStateStr, getStartIndex(), getPageSizeVal());
113131
} else {
114-
roles = roleService.listRoles(getStartIndex(), getPageSizeVal());
132+
roles = roleService.listRoles(roleStateStr, getStartIndex(), getPageSizeVal());
115133
}
116134
setupResponse(roles);
117135
}

api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ protected void setupResponse(final Role role) {
5959
response.setRoleType(role.getRoleType());
6060
response.setDescription(role.getDescription());
6161
response.setPublicRole(role.isPublicRole());
62+
response.setState(role.getState().toString());
6263
response.setResponseName(getCommandName());
6364
response.setObjectName("role");
6465
setResponseObject(response);

api/src/main/java/org/apache/cloudstack/api/response/RoleResponse.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ public class RoleResponse extends BaseRoleResponse {
3636
@Param(description = "true if role is default, false otherwise")
3737
private Boolean isDefault;
3838

39+
@SerializedName(ApiConstants.STATE)
40+
@Param(description = "the state of the role")
41+
private String state;
42+
3943
public void setRoleType(RoleType roleType) {
4044
if (roleType != null) {
4145
this.roleType = roleType.name();
@@ -45,4 +49,8 @@ public void setRoleType(RoleType roleType) {
4549
public void setIsDefault(Boolean isDefault) {
4650
this.isDefault = isDefault;
4751
}
52+
53+
public void setState(String state) {
54+
this.state = state;
55+
}
4856
}

api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public void testCreateRoleWithRoleType() {
5454
when(role.getDescription()).thenReturn("User test");
5555
when(role.getName()).thenReturn("testuser");
5656
when(role.getRoleType()).thenReturn(RoleType.User);
57+
when(role.getState()).thenReturn(Role.State.ENABLED);
5758
when(roleService.createRole(createRoleCmd.getRoleName(), createRoleCmd.getRoleType(), createRoleCmd.getRoleDescription(), true)).thenReturn(role);
5859
createRoleCmd.execute();
5960
RoleResponse response = (RoleResponse) createRoleCmd.getResponseObject();
@@ -71,6 +72,7 @@ public void testCreateRoleWithExistingRole() {
7172
when(newRole.getDescription()).thenReturn("User test");
7273
when(newRole.getName()).thenReturn("testuser");
7374
when(newRole.getRoleType()).thenReturn(RoleType.User);
75+
when(newRole.getState()).thenReturn(Role.State.ENABLED);
7476
when(roleService.createRole(createRoleCmd.getRoleName(), role, createRoleCmd.getRoleDescription(), true)).thenReturn(newRole);
7577
createRoleCmd.execute();
7678
RoleResponse response = (RoleResponse) createRoleCmd.getResponseObject();

api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public void testImportRoleSuccess() {
8787
when(role.getDescription()).thenReturn("test user imported");
8888
when(role.getName()).thenReturn("Test User");
8989
when(role.getRoleType()).thenReturn(RoleType.User);
90+
when(role.getState()).thenReturn(Role.State.ENABLED);
9091
when(roleService.importRole(anyString(), any(), anyString(), any(), anyBoolean(), anyBoolean())).thenReturn(role);
9192

9293
importRoleCmd.execute();

0 commit comments

Comments
 (0)