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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions api/src/main/java/com/cloud/event/EventTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ public class EventTypes {
public static final String EVENT_ROLE_UPDATE = "ROLE.UPDATE";
public static final String EVENT_ROLE_DELETE = "ROLE.DELETE";
public static final String EVENT_ROLE_IMPORT = "ROLE.IMPORT";
public static final String EVENT_ROLE_ENABLE = "ROLE.ENABLE";
public static final String EVENT_ROLE_DISABLE = "ROLE.DISABLE";
public static final String EVENT_ROLE_PERMISSION_CREATE = "ROLE.PERMISSION.CREATE";
public static final String EVENT_ROLE_PERMISSION_UPDATE = "ROLE.PERMISSION.UPDATE";
public static final String EVENT_ROLE_PERMISSION_DELETE = "ROLE.PERMISSION.DELETE";
Expand Down Expand Up @@ -841,6 +843,8 @@ public class EventTypes {
entityEventDetails.put(EVENT_ROLE_UPDATE, Role.class);
entityEventDetails.put(EVENT_ROLE_DELETE, Role.class);
entityEventDetails.put(EVENT_ROLE_IMPORT, Role.class);
entityEventDetails.put(EVENT_ROLE_ENABLE, Role.class);
entityEventDetails.put(EVENT_ROLE_DISABLE, Role.class);
entityEventDetails.put(EVENT_ROLE_PERMISSION_CREATE, RolePermission.class);
entityEventDetails.put(EVENT_ROLE_PERMISSION_UPDATE, RolePermission.class);
entityEventDetails.put(EVENT_ROLE_PERMISSION_DELETE, RolePermission.class);
Expand Down
11 changes: 11 additions & 0 deletions api/src/main/java/org/apache/cloudstack/acl/Role.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,18 @@
import org.apache.cloudstack.api.InternalIdentity;

public interface Role extends RoleEntity, InternalIdentity, Identity {

enum State {
ENABLED, DISABLED;

@Override
public String toString(){
return super.toString().toLowerCase();
}
}

RoleType getRoleType();
boolean isDefault();
boolean isPublicRole();
State getState();
}
10 changes: 7 additions & 3 deletions api/src/main/java/org/apache/cloudstack/acl/RoleService.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public interface RoleService {

boolean deleteRole(Role role);

boolean enableRole(Role role);

boolean disableRole(Role role);

RolePermission findRolePermission(Long id);

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

Pair<List<Role>, Integer> listRoles(Long startIndex, Long limit);
Pair<List<Role>, Integer> listRoles(String state, Long startIndex, Long limit);

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

Pair<List<Role>, Integer> findRolesByName(String name, String keyword, Long startIndex, Long limit);
Pair<List<Role>, Integer> findRolesByName(String name, String keyword, String state, Long startIndex, Long limit);

/**
* 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.
*/
List<Role> findRolesByType(RoleType roleType);

Pair<List<Role>, Integer> findRolesByType(RoleType roleType, Long startIndex, Long limit);
Pair<List<Role>, Integer> findRolesByType(RoleType roleType, String state, Long startIndex, Long limit);

List<RolePermission> findAllPermissionsBy(Long roleId);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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.
package org.apache.cloudstack.api.command.admin.acl;

import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.user.Account;
import org.apache.cloudstack.acl.Role;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiArgValidator;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.RoleResponse;
import org.apache.cloudstack.api.response.SuccessResponse;
import org.apache.cloudstack.context.CallContext;

@APICommand(name = "disableRole", description = "Disables a role", responseObject = SuccessResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
since = "4.20.0",
authorized = {RoleType.Admin})
public class DisableRoleCmd extends BaseCmd {

@Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, required = true, entityType = RoleResponse.class,
description = "ID of the role", validations = {ApiArgValidator.PositiveNumber})
private Long roleId;

public Long getRoleId() {
return roleId;
}

@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
Role role = roleService.findRole(getRoleId());
if (role == null) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Cannot find the role with provided id");
}
CallContext.current().setEventDetails("Role id: " + role.getId());
boolean result = roleService.disableRole(role);
SuccessResponse response = new SuccessResponse(getCommandName());
response.setSuccess(result);
setResponseObject(response);
}

@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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.
package org.apache.cloudstack.api.command.admin.acl;

import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.user.Account;
import org.apache.cloudstack.acl.Role;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiArgValidator;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.RoleResponse;
import org.apache.cloudstack.api.response.SuccessResponse;
import org.apache.cloudstack.context.CallContext;

@APICommand(name = "enableRole", description = "Enables a role", responseObject = SuccessResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
since = "4.20.0",
authorized = {RoleType.Admin})
public class EnableRoleCmd extends BaseCmd {

@Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, required = true, entityType = RoleResponse.class,
description = "ID of the role", validations = {ApiArgValidator.PositiveNumber})
private Long roleId;

public Long getRoleId() {
return roleId;
}

@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
Role role = roleService.findRole(getRoleId());
if (role == null) {
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Cannot find the role with provided id");
}
CallContext.current().setEventDetails("Role id: " + role.getId());
boolean result = roleService.enableRole(role);
SuccessResponse response = new SuccessResponse(getCommandName());
response.setSuccess(result);
setResponseObject(response);
}

@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Collections;
import java.util.List;

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

@Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "List role by role type status, valid options are: enabled, disabled")
private String roleState;

/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
Expand All @@ -70,6 +74,17 @@ public RoleType getRoleType() {
return null;
}

public Role.State getRoleState() {
if (roleState == null) {
return null;
}
try {
return Role.State.valueOf(roleState.toUpperCase());
} catch (IllegalArgumentException e) {
throw new InvalidParameterValueException("Unrecognized role state value");
}
}

/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
Expand All @@ -93,6 +108,7 @@ private void setupResponse(final Pair<List<Role>, Integer> roles) {
roleResponse.setDescription(role.getDescription());
roleResponse.setIsDefault(role.isDefault());
roleResponse.setPublicRole(role.isPublicRole());
roleResponse.setState(role.getState().toString());
roleResponse.setObjectName("role");
roleResponses.add(roleResponse);
}
Expand All @@ -104,14 +120,16 @@ private void setupResponse(final Pair<List<Role>, Integer> roles) {
@Override
public void execute() {
Pair<List<Role>, Integer> roles;
Role.State state = getRoleState();
String roleStateStr = state != null ? state.toString() : null;
if (getId() != null && getId() > 0L) {
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());
roles = roleService.findRolesByName(getName(), getKeyword(), roleStateStr, getStartIndex(), getPageSizeVal());
} else if (getRoleType() != null) {
roles = roleService.findRolesByType(getRoleType(), getStartIndex(), getPageSizeVal());
roles = roleService.findRolesByType(getRoleType(), roleStateStr, getStartIndex(), getPageSizeVal());
} else {
roles = roleService.listRoles(getStartIndex(), getPageSizeVal());
roles = roleService.listRoles(roleStateStr, getStartIndex(), getPageSizeVal());
}
setupResponse(roles);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ protected void setupResponse(final Role role) {
response.setRoleType(role.getRoleType());
response.setDescription(role.getDescription());
response.setPublicRole(role.isPublicRole());
response.setState(role.getState().toString());
response.setResponseName(getCommandName());
response.setObjectName("role");
setResponseObject(response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ public class RoleResponse extends BaseRoleResponse {
@Param(description = "true if role is default, false otherwise")
private Boolean isDefault;

@SerializedName(ApiConstants.STATE)
@Param(description = "the state of the role")
private String state;

public void setRoleType(RoleType roleType) {
if (roleType != null) {
this.roleType = roleType.name();
Expand All @@ -45,4 +49,8 @@ public void setRoleType(RoleType roleType) {
public void setIsDefault(Boolean isDefault) {
this.isDefault = isDefault;
}

public void setState(String state) {
this.state = state;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public void testCreateRoleWithRoleType() {
when(role.getDescription()).thenReturn("User test");
when(role.getName()).thenReturn("testuser");
when(role.getRoleType()).thenReturn(RoleType.User);
when(role.getState()).thenReturn(Role.State.ENABLED);
when(roleService.createRole(createRoleCmd.getRoleName(), createRoleCmd.getRoleType(), createRoleCmd.getRoleDescription(), true)).thenReturn(role);
createRoleCmd.execute();
RoleResponse response = (RoleResponse) createRoleCmd.getResponseObject();
Expand All @@ -71,6 +72,7 @@ public void testCreateRoleWithExistingRole() {
when(newRole.getDescription()).thenReturn("User test");
when(newRole.getName()).thenReturn("testuser");
when(newRole.getRoleType()).thenReturn(RoleType.User);
when(newRole.getState()).thenReturn(Role.State.ENABLED);
when(roleService.createRole(createRoleCmd.getRoleName(), role, createRoleCmd.getRoleDescription(), true)).thenReturn(newRole);
createRoleCmd.execute();
RoleResponse response = (RoleResponse) createRoleCmd.getResponseObject();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public void testImportRoleSuccess() {
when(role.getDescription()).thenReturn("test user imported");
when(role.getName()).thenReturn("Test User");
when(role.getRoleType()).thenReturn(RoleType.User);
when(role.getState()).thenReturn(Role.State.ENABLED);
when(roleService.importRole(anyString(), any(), anyString(), any(), anyBoolean(), anyBoolean())).thenReturn(role);

importRoleCmd.execute();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public void testUpdateSuccess() {
when(role.getId()).thenReturn(1L);
when(role.getDescription()).thenReturn("Description Initial");
when(role.getName()).thenReturn("User");
when(role.getState()).thenReturn(Role.State.ENABLED);
updateRoleCmd.execute();
RoleResponse response = (RoleResponse) updateRoleCmd.getResponseObject();
assertEquals((String)ReflectionTestUtils.getField(response, "roleName"),role.getName());
Expand Down
13 changes: 13 additions & 0 deletions engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,16 @@ public class RoleVO implements Role {
@Column(name = "public_role")
private boolean publicRole = true;

@Column(name = "state")
@Enumerated(value = EnumType.STRING)
private State state;

@Column(name = GenericDao.REMOVED_COLUMN)
private Date removed;

public RoleVO() {
this.uuid = UUID.randomUUID().toString();
this.state = State.ENABLED;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no constructor needed with the actual state (from the DB or UI)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DaanHoogland if I understand your question correctly, this is needed as the state DB column needs a non-null value, so each time a new role is created it is enabled by default.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, I'd expect the DAO to instantiate a unmarshalled Role with state disabled if it was so when saved. But maybe accessors are enough for that.

}

public RoleVO(final String name, final RoleType roleType, final String description) {
Expand Down Expand Up @@ -131,4 +136,12 @@ public boolean isPublicRole() {
public void setPublicRole(boolean publicRole) {
this.publicRole = publicRole;
}

public State getState() {
return state;
}

public void setState(State state) {
this.state = state;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@
public interface RoleDao extends GenericDao<RoleVO, Long> {
List<RoleVO> findAllByName(String roleName, boolean showPrivateRole);

Pair<List<RoleVO>, Integer> findAllByName(final String roleName, String keyword, Long offset, Long limit, boolean showPrivateRole);
Pair<List<RoleVO>, Integer> findAllByName(final String roleName, String keyword, String state, Long offset, Long limit, boolean showPrivateRole);

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, boolean showPrivateRole);
Pair<List<RoleVO>, Integer> findAllByRoleType(RoleType type, String state, Long offset, Long limit, boolean showPrivateRole);

Pair<List<RoleVO>, Integer> listAllRoles(Long startIndex, Long limit, boolean showPrivateRole);
Pair<List<RoleVO>, Integer> listAllRoles(String state, Long startIndex, Long limit, boolean showPrivateRole);

List<RoleVO> searchByIds(Long... ids);
}
Loading