mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
server: Dynamic roles improvements. Add-on functionality below. (#4071)
- Create a role from any of the existing role, using new parameter roleid in createRole API - Import a role with its rules, using a new importRole API - New default roles for Read-Only and Support Admin & User - No modifications allowed for Default roles - Cleaned up old NetApp APIs from role_permissions table.
This commit is contained in:
parent
a73712ec4e
commit
5040283db9
@ -185,6 +185,7 @@ public class EventTypes {
|
||||
public static final String EVENT_ROLE_CREATE = "ROLE.CREATE";
|
||||
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_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";
|
||||
@ -691,6 +692,7 @@ public class EventTypes {
|
||||
entityEventDetails.put(EVENT_ROLE_CREATE, Role.class);
|
||||
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_PERMISSION_CREATE, RolePermission.class);
|
||||
entityEventDetails.put(EVENT_ROLE_PERMISSION_UPDATE, RolePermission.class);
|
||||
entityEventDetails.put(EVENT_ROLE_PERMISSION_DELETE, RolePermission.class);
|
||||
|
||||
@ -24,4 +24,5 @@ public interface Role extends InternalIdentity, Identity {
|
||||
String getName();
|
||||
RoleType getRoleType();
|
||||
String getDescription();
|
||||
boolean isDefault();
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
package org.apache.cloudstack.acl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.acl.RolePermission.Permission;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
@ -39,13 +40,17 @@ public interface RoleService {
|
||||
|
||||
Role createRole(String name, RoleType roleType, String description);
|
||||
|
||||
Role createRole(String name, Role role, String description);
|
||||
|
||||
Role importRole(String name, RoleType roleType, String description, List<Map<String, Object>> rules, boolean forced);
|
||||
|
||||
Role updateRole(Role role, String name, RoleType roleType, String description);
|
||||
|
||||
boolean deleteRole(Role role);
|
||||
|
||||
RolePermission findRolePermission(Long id);
|
||||
|
||||
RolePermission findRolePermissionByUuid(String uuid);
|
||||
RolePermission findRolePermissionByRoleIdAndRule(Long roleId, String rule);
|
||||
|
||||
RolePermission createRolePermission(Role role, Rule rule, Permission permission, String description);
|
||||
|
||||
@ -77,4 +82,6 @@ public interface RoleService {
|
||||
List<Role> findRolesByType(RoleType roleType);
|
||||
|
||||
List<RolePermission> findAllPermissionsBy(Long roleId);
|
||||
|
||||
Permission getRolePermission(String permission);
|
||||
}
|
||||
|
||||
@ -478,6 +478,7 @@ public class ApiConstants {
|
||||
public static final String ROLE_NAME = "rolename";
|
||||
public static final String PERMISSION = "permission";
|
||||
public static final String RULE = "rule";
|
||||
public static final String RULES = "rules";
|
||||
public static final String RULE_ID = "ruleid";
|
||||
public static final String RULE_ORDER = "ruleorder";
|
||||
public static final String USER = "user";
|
||||
|
||||
@ -43,4 +43,5 @@ public interface ApiServerService {
|
||||
|
||||
public Class<?> getCmdClass(String cmdName);
|
||||
|
||||
public boolean isValidApiName(String apiName);
|
||||
}
|
||||
|
||||
@ -45,13 +45,9 @@ public class CreateRoleCmd extends RoleCmd {
|
||||
description = "creates a role with this unique name", validations = {ApiArgValidator.NotNullOrEmpty})
|
||||
private String roleName;
|
||||
|
||||
@Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = true,
|
||||
description = "The type of the role, valid options are: Admin, ResourceAdmin, DomainAdmin, User",
|
||||
validations = {ApiArgValidator.NotNullOrEmpty})
|
||||
private String roleType;
|
||||
|
||||
@Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the role")
|
||||
private String roleDescription;
|
||||
@Parameter(name = ApiConstants.ROLE_ID, type = CommandType.UUID, entityType = RoleResponse.class,
|
||||
description = "ID of the role to be cloned from. Either roleid or type must be passed in")
|
||||
private Long roleId;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
@ -61,12 +57,8 @@ public class CreateRoleCmd extends RoleCmd {
|
||||
return roleName;
|
||||
}
|
||||
|
||||
public RoleType getRoleType() {
|
||||
return RoleType.fromString(roleType);
|
||||
}
|
||||
|
||||
public String getRoleDescription() {
|
||||
return roleDescription;
|
||||
public Long getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
@ -85,11 +77,39 @@ public class CreateRoleCmd extends RoleCmd {
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
CallContext.current().setEventDetails("Role: " + getRoleName() + ", type:" + getRoleType() + ", description: " + getRoleDescription());
|
||||
final Role role = roleService.createRole(getRoleName(), getRoleType(), getRoleDescription());
|
||||
validateRoleParameters();
|
||||
|
||||
Role role = null;
|
||||
if (getRoleId() != null) {
|
||||
Role existingRole = roleService.findRole(getRoleId());
|
||||
if (existingRole == null) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role id provided");
|
||||
}
|
||||
|
||||
CallContext.current().setEventDetails("Role: " + getRoleName() + ", from role: " + getRoleId() + ", description: " + getRoleDescription());
|
||||
role = roleService.createRole(getRoleName(), existingRole, getRoleDescription());
|
||||
} else {
|
||||
CallContext.current().setEventDetails("Role: " + getRoleName() + ", type: " + getRoleType() + ", description: " + getRoleDescription());
|
||||
role = roleService.createRole(getRoleName(), getRoleType(), getRoleDescription());
|
||||
}
|
||||
|
||||
if (role == null) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create role");
|
||||
}
|
||||
setupResponse(role);
|
||||
}
|
||||
|
||||
private void validateRoleParameters() {
|
||||
if (getRoleType() == null && getRoleId() == null) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Neither role type nor role ID is provided");
|
||||
}
|
||||
|
||||
if (getRoleType() != null && getRoleId() != null) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Both role type and role ID should not be specified");
|
||||
}
|
||||
|
||||
if (getRoleId() != null && getRoleId() < 1L) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role id provided");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,149 @@
|
||||
// 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 java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.Rule;
|
||||
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.ApiServerService;
|
||||
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.context.CallContext;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
|
||||
import com.cloud.user.Account;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
@APICommand(name = ImportRoleCmd.APINAME, description = "Imports a role based on provided map of rule permissions", responseObject = RoleResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
|
||||
since = "4.15.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class ImportRoleCmd extends RoleCmd {
|
||||
public static final String APINAME = "importRole";
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true,
|
||||
description = "Creates a role with this unique name", validations = {ApiArgValidator.NotNullOrEmpty})
|
||||
private String roleName;
|
||||
|
||||
@Parameter(name = ApiConstants.RULES, type = CommandType.MAP, required = true,
|
||||
description = "Rules param list, rule and permission is must. Example: rules[0].rule=create*&rules[0].permission=allow&rules[0].description=create%20rule&rules[1].rule=list*&rules[1].permission=allow&rules[1].description=listing")
|
||||
private Map rules;
|
||||
|
||||
@Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN,
|
||||
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;
|
||||
|
||||
@Inject
|
||||
ApiServerService _apiServer;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
public String getRoleName() {
|
||||
return roleName;
|
||||
}
|
||||
|
||||
// Returns list of rule maps. Each map corresponds to a rule with the details in the keys: rule, permission & description
|
||||
public List<Map<String, Object>> getRules() {
|
||||
if (MapUtils.isEmpty(rules)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Map<String, Object>> rulesDetails = new ArrayList<>();
|
||||
Collection rulesCollection = rules.values();
|
||||
Iterator iter = rulesCollection.iterator();
|
||||
while (iter.hasNext()) {
|
||||
HashMap<String, String> detail = (HashMap<String, String>)iter.next();
|
||||
Map<String, Object> ruleDetails = new HashMap<>();
|
||||
String rule = detail.get(ApiConstants.RULE);
|
||||
if (Strings.isNullOrEmpty(rule)) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Empty rule provided in rules param");
|
||||
}
|
||||
if (!rule.contains("*") && !_apiServer.isValidApiName(rule)) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid api name: " + rule + " provided in rules param");
|
||||
}
|
||||
ruleDetails.put(ApiConstants.RULE, new Rule(rule));
|
||||
|
||||
String permission = detail.get(ApiConstants.PERMISSION);
|
||||
if (Strings.isNullOrEmpty(permission)) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid permission: "+ permission + " provided in rules param");
|
||||
}
|
||||
ruleDetails.put(ApiConstants.PERMISSION, roleService.getRolePermission(permission));
|
||||
|
||||
String description = detail.get(ApiConstants.DESCRIPTION);
|
||||
if (!Strings.isNullOrEmpty(permission)) {
|
||||
ruleDetails.put(ApiConstants.DESCRIPTION, description);
|
||||
}
|
||||
|
||||
rulesDetails.add(ruleDetails);
|
||||
}
|
||||
return rulesDetails;
|
||||
}
|
||||
|
||||
public boolean isForced() {
|
||||
return (forced != null) ? forced : false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if (getRoleType() == null) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role type provided");
|
||||
}
|
||||
|
||||
CallContext.current().setEventDetails("Role: " + getRoleName() + ", type: " + getRoleType() + ", description: " + getRoleDescription());
|
||||
Role role = roleService.importRole(getRoleName(), getRoleType(), getRoleDescription(), getRules(), isForced());
|
||||
if (role == null) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to import role");
|
||||
}
|
||||
setupResponse(role);
|
||||
}
|
||||
}
|
||||
@ -97,6 +97,7 @@ public class ListRolesCmd extends BaseCmd {
|
||||
roleResponse.setRoleName(role.getName());
|
||||
roleResponse.setRoleType(role.getRoleType());
|
||||
roleResponse.setDescription(role.getDescription());
|
||||
roleResponse.setIsDefault(role.isDefault());
|
||||
roleResponse.setObjectName("role");
|
||||
roleResponses.add(roleResponse);
|
||||
}
|
||||
|
||||
@ -18,11 +18,41 @@
|
||||
package org.apache.cloudstack.api.command.admin.acl;
|
||||
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.RoleResponse;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
public abstract class RoleCmd extends BaseCmd {
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "The type of the role, valid options are: Admin, ResourceAdmin, DomainAdmin, User")
|
||||
private String roleType;
|
||||
|
||||
@Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the role")
|
||||
private String roleDescription;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
public RoleType getRoleType() {
|
||||
if (!Strings.isNullOrEmpty(roleType)) {
|
||||
return RoleType.fromString(roleType);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getRoleDescription() {
|
||||
return roleDescription;
|
||||
}
|
||||
|
||||
protected void setupResponse(final Role role) {
|
||||
final RoleResponse response = new RoleResponse();
|
||||
response.setId(role.getUuid());
|
||||
|
||||
@ -18,7 +18,6 @@
|
||||
package org.apache.cloudstack.api.command.admin.acl;
|
||||
|
||||
import com.cloud.user.Account;
|
||||
import com.google.common.base.Strings;
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
@ -49,9 +48,6 @@ public class UpdateRoleCmd extends RoleCmd {
|
||||
@Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, description = "creates a role with this unique name")
|
||||
private String roleName;
|
||||
|
||||
@Parameter(name = ApiConstants.TYPE, type = BaseCmd.CommandType.STRING, description = "The type of the role, valid options are: Admin, ResourceAdmin, DomainAdmin, User")
|
||||
private String roleType;
|
||||
|
||||
@Parameter(name = ApiConstants.DESCRIPTION, type = BaseCmd.CommandType.STRING, description = "The description of the role")
|
||||
private String roleDescription;
|
||||
|
||||
@ -67,17 +63,6 @@ public class UpdateRoleCmd extends RoleCmd {
|
||||
return roleName;
|
||||
}
|
||||
|
||||
public RoleType getRoleType() {
|
||||
if (!Strings.isNullOrEmpty(roleType)) {
|
||||
return RoleType.fromString(roleType);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getRoleDescription() {
|
||||
return roleDescription;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@ -43,6 +43,10 @@ public class RoleResponse extends BaseResponse {
|
||||
@Param(description = "the description of the role")
|
||||
private String roleDescription;
|
||||
|
||||
@SerializedName(ApiConstants.IS_DEFAULT)
|
||||
@Param(description = "true if role is default, false otherwise")
|
||||
private Boolean isDefault;
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
@ -60,4 +64,8 @@ public class RoleResponse extends BaseResponse {
|
||||
public void setDescription(String description) {
|
||||
this.roleDescription = description;
|
||||
}
|
||||
|
||||
public void setIsDefault(Boolean isDefault) {
|
||||
this.isDefault = isDefault;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,102 @@
|
||||
// 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.test;
|
||||
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RoleService;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.command.admin.acl.CreateRoleCmd;
|
||||
import org.apache.cloudstack.api.response.RoleResponse;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class CreateRoleCmdTest {
|
||||
private CreateRoleCmd createRoleCmd;
|
||||
private RoleService roleService;
|
||||
private Role role;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
roleService = Mockito.spy(RoleService.class);
|
||||
createRoleCmd = new CreateRoleCmd();
|
||||
ReflectionTestUtils.setField(createRoleCmd,"roleService",roleService);
|
||||
ReflectionTestUtils.setField(createRoleCmd,"roleName","testuser");
|
||||
ReflectionTestUtils.setField(createRoleCmd,"roleDescription","User test");
|
||||
role = Mockito.mock(Role.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRoleWithRoleType() {
|
||||
ReflectionTestUtils.setField(createRoleCmd,"roleType", "User");
|
||||
when(role.getId()).thenReturn(1L);
|
||||
when(role.getUuid()).thenReturn("12345-abcgdkajd");
|
||||
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);
|
||||
createRoleCmd.execute();
|
||||
RoleResponse response = (RoleResponse) createRoleCmd.getResponseObject();
|
||||
Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleName"), role.getName());
|
||||
Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleDescription"), role.getDescription());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRoleWithExistingRole() {
|
||||
ReflectionTestUtils.setField(createRoleCmd,"roleId",1L);
|
||||
when(roleService.findRole(createRoleCmd.getRoleId())).thenReturn(role);
|
||||
Role newRole = Mockito.mock(Role.class);
|
||||
when(newRole.getId()).thenReturn(2L);
|
||||
when(newRole.getUuid()).thenReturn("67890-xyztestid");
|
||||
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);
|
||||
createRoleCmd.execute();
|
||||
RoleResponse response = (RoleResponse) createRoleCmd.getResponseObject();
|
||||
Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleName"), newRole.getName());
|
||||
Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleDescription"), newRole.getDescription());
|
||||
}
|
||||
|
||||
@Test(expected = ServerApiException.class)
|
||||
public void testCreateRoleWithNonExistingRole() {
|
||||
ReflectionTestUtils.setField(createRoleCmd,"roleId",1L);
|
||||
when(roleService.findRole(createRoleCmd.getRoleId())).thenReturn(null);
|
||||
createRoleCmd.execute();
|
||||
Assert.fail("An exception should have been thrown: " + ServerApiException.class);
|
||||
}
|
||||
|
||||
@Test(expected = ServerApiException.class)
|
||||
public void testCreateRoleValidateNeitherRoleIdNorTypeParameters() {
|
||||
createRoleCmd.execute();
|
||||
Assert.fail("An exception should have been thrown: " + ServerApiException.class);
|
||||
}
|
||||
|
||||
@Test(expected = ServerApiException.class)
|
||||
public void testCreateRoleValidateBothRoleIdAndTypeParameters() {
|
||||
ReflectionTestUtils.setField(createRoleCmd,"roleId",1L);
|
||||
ReflectionTestUtils.setField(createRoleCmd,"roleType", "User");
|
||||
createRoleCmd.execute();
|
||||
Assert.fail("An exception should have been thrown: " + ServerApiException.class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,131 @@
|
||||
// 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.test;
|
||||
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RoleService;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.command.admin.acl.ImportRoleCmd;
|
||||
import org.apache.cloudstack.api.response.RoleResponse;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyCollection;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
public class ImportRoleCmdTest {
|
||||
private ImportRoleCmd importRoleCmd;
|
||||
private RoleService roleService;
|
||||
private Role role;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
roleService = Mockito.spy(RoleService.class);
|
||||
importRoleCmd = new ImportRoleCmd();
|
||||
ReflectionTestUtils.setField(importRoleCmd,"roleService",roleService);
|
||||
ReflectionTestUtils.setField(importRoleCmd,"roleName","Test User");
|
||||
ReflectionTestUtils.setField(importRoleCmd,"roleType", "User");
|
||||
ReflectionTestUtils.setField(importRoleCmd,"roleDescription","test user imported");
|
||||
role = Mockito.mock(Role.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportRoleSuccess() {
|
||||
Map<String, Map<String, String>> rules = new HashMap<String, Map<String, String>>();
|
||||
|
||||
//Rule 1
|
||||
Map<String, String> rule1 = new HashMap<String, String>();
|
||||
rule1.put(ApiConstants.RULE, "list*");
|
||||
rule1.put(ApiConstants.PERMISSION, "allow");
|
||||
rule1.put(ApiConstants.DESCRIPTION, "listing apis");
|
||||
rules.put("key1", rule1);
|
||||
|
||||
//Rule 2
|
||||
Map<String, String> rule2 = new HashMap<String, String>();
|
||||
rule2.put(ApiConstants.RULE, "update*");
|
||||
rule2.put(ApiConstants.PERMISSION, "deny");
|
||||
rule2.put(ApiConstants.DESCRIPTION, "no update allowed");
|
||||
rules.put("key2", rule2);
|
||||
|
||||
//Rule 3
|
||||
Map<String, String> rule3 = new HashMap<String, String>();
|
||||
rule3.put(ApiConstants.RULE, "get*");
|
||||
rule3.put(ApiConstants.PERMISSION, "allow");
|
||||
rule3.put(ApiConstants.DESCRIPTION, "get details");
|
||||
rules.put("key3", rule3);
|
||||
|
||||
ReflectionTestUtils.setField(importRoleCmd,"rules",rules);
|
||||
|
||||
when(role.getUuid()).thenReturn("12345-abcgdkajd");
|
||||
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);
|
||||
|
||||
importRoleCmd.execute();
|
||||
RoleResponse response = (RoleResponse) importRoleCmd.getResponseObject();
|
||||
Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleName"), role.getName());
|
||||
Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleDescription"), role.getDescription());
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testImportRoleInvalidRule() {
|
||||
Map<String, Map<String, String>> rules = new HashMap<String, Map<String, String>>();
|
||||
Map<String, String> rule = new HashMap<String, String>();
|
||||
rule.put(ApiConstants.RULE, "*?+test*");
|
||||
rule.put(ApiConstants.PERMISSION, "allow");
|
||||
rule.put(ApiConstants.DESCRIPTION, "listing apis");
|
||||
rules.put("key1", rule);
|
||||
ReflectionTestUtils.setField(importRoleCmd,"rules",rules);
|
||||
|
||||
importRoleCmd.execute();
|
||||
Assert.fail("An exception should have been thrown: " + InvalidParameterValueException.class);
|
||||
}
|
||||
|
||||
@Test(expected = ServerApiException.class)
|
||||
public void testImportRoleInvalidPermission() {
|
||||
Map<String, Map<String, String>> rules = new HashMap<String, Map<String, String>>();
|
||||
Map<String, String> rule = new HashMap<String, String>();
|
||||
rule.put(ApiConstants.RULE, "list*");
|
||||
rule.put(ApiConstants.PERMISSION, "pass");
|
||||
rule.put(ApiConstants.DESCRIPTION, "listing apis");
|
||||
rules.put("key1", rule);
|
||||
ReflectionTestUtils.setField(importRoleCmd,"rules",rules);
|
||||
|
||||
importRoleCmd.execute();
|
||||
Assert.fail("An exception should have been thrown: " + ServerApiException.class);
|
||||
}
|
||||
}
|
||||
@ -22,8 +22,11 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@ -65,6 +68,7 @@ public class Upgrade41400to41500 implements DbUpgrade {
|
||||
@Override
|
||||
public void performDataMigration(Connection conn) {
|
||||
updateSystemVmTemplates(conn);
|
||||
addRolePermissionsForNewReadOnlyAndSupportRoles(conn);
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@ -235,6 +239,288 @@ public class Upgrade41400to41500 implements DbUpgrade {
|
||||
LOG.debug("Updating System Vm Template IDs Complete");
|
||||
}
|
||||
|
||||
private void addRolePermissionsForNewReadOnlyAndSupportRoles(final Connection conn) {
|
||||
addRolePermissionsForReadOnlyAdmin(conn);
|
||||
addRolePermissionsForReadOnlyUser(conn);
|
||||
addRolePermissionsForSupportAdmin(conn);
|
||||
addRolePermissionsForSupportUser(conn);
|
||||
}
|
||||
|
||||
private void addRolePermissionsForReadOnlyAdmin(final Connection conn) {
|
||||
LOG.debug("Adding role permissions for new read-only admin role");
|
||||
try {
|
||||
PreparedStatement pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`roles` WHERE name = 'Read-Only Admin - Default' AND is_default = 1");
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
long readOnlyAdminRoleId = rs.getLong(1);
|
||||
int readOnlyAdminSortOrder = 0;
|
||||
Map<String, String> readOnlyAdminRules = new LinkedHashMap<>();
|
||||
readOnlyAdminRules.put("list*", "ALLOW");
|
||||
readOnlyAdminRules.put("getUploadParamsFor*", "DENY");
|
||||
readOnlyAdminRules.put("get*", "ALLOW");
|
||||
readOnlyAdminRules.put("cloudianIsEnabled", "ALLOW");
|
||||
readOnlyAdminRules.put("queryAsyncJobResult", "ALLOW");
|
||||
readOnlyAdminRules.put("quotaIsEnabled", "ALLOW");
|
||||
readOnlyAdminRules.put("quotaTariffList", "ALLOW");
|
||||
readOnlyAdminRules.put("quotaSummary", "ALLOW");
|
||||
readOnlyAdminRules.put("*", "DENY");
|
||||
|
||||
for (Map.Entry<String, String> readOnlyAdminRule : readOnlyAdminRules.entrySet()) {
|
||||
pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, ?, ?) ON DUPLICATE KEY UPDATE rule=rule");
|
||||
pstmt.setLong(1, readOnlyAdminRoleId);
|
||||
pstmt.setString(2, readOnlyAdminRule.getKey());
|
||||
pstmt.setString(3, readOnlyAdminRule.getValue());
|
||||
pstmt.setLong(4, readOnlyAdminSortOrder++);
|
||||
pstmt.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
if (rs != null && !rs.isClosed()) {
|
||||
rs.close();
|
||||
}
|
||||
if (pstmt != null && !pstmt.isClosed()) {
|
||||
pstmt.close();
|
||||
}
|
||||
LOG.debug("Successfully added role permissions for new read-only admin role");
|
||||
} catch (final SQLException e) {
|
||||
LOG.error("Exception while adding role permissions for read-only admin role: " + e.getMessage());
|
||||
throw new CloudRuntimeException("Exception while adding role permissions for read-only admin role: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addRolePermissionsForReadOnlyUser(final Connection conn) {
|
||||
LOG.debug("Adding role permissions for new read-only user role");
|
||||
try {
|
||||
PreparedStatement pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`roles` WHERE name = 'Read-Only User - Default' AND is_default = 1");
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
long readOnlyUserRoleId = rs.getLong(1);
|
||||
int readOnlyUserSortOrder = 0;
|
||||
|
||||
pstmt = conn.prepareStatement("SELECT rule FROM `cloud`.`role_permissions` WHERE role_id = 4 AND permission = 'ALLOW' AND rule LIKE 'list%' ORDER BY sort_order");
|
||||
ResultSet rsRolePermissions = pstmt.executeQuery();
|
||||
|
||||
while (rsRolePermissions.next()) {
|
||||
String rule = rsRolePermissions.getString(1);
|
||||
pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, 'ALLOW', ?) ON DUPLICATE KEY UPDATE rule=rule");
|
||||
pstmt.setLong(1, readOnlyUserRoleId);
|
||||
pstmt.setString(2, rule);
|
||||
pstmt.setLong(3, readOnlyUserSortOrder++);
|
||||
pstmt.executeUpdate();
|
||||
}
|
||||
|
||||
pstmt = conn.prepareStatement("SELECT rule FROM `cloud`.`role_permissions` WHERE role_id = 4 AND permission = 'ALLOW' AND rule LIKE 'get%' AND rule NOT LIKE 'getUploadParamsFor%' ORDER BY sort_order");
|
||||
rsRolePermissions = pstmt.executeQuery();
|
||||
|
||||
while (rsRolePermissions.next()) {
|
||||
String rule = rsRolePermissions.getString(1);
|
||||
pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, 'ALLOW', ?) ON DUPLICATE KEY UPDATE rule=rule");
|
||||
pstmt.setLong(1, readOnlyUserRoleId);
|
||||
pstmt.setString(2, rule);
|
||||
pstmt.setLong(3, readOnlyUserSortOrder++);
|
||||
pstmt.executeUpdate();
|
||||
}
|
||||
|
||||
List<String> readOnlyUserRulesAllowed = new ArrayList<>();
|
||||
readOnlyUserRulesAllowed.add("cloudianIsEnabled");
|
||||
readOnlyUserRulesAllowed.add("queryAsyncJobResult");
|
||||
readOnlyUserRulesAllowed.add("quotaIsEnabled");
|
||||
readOnlyUserRulesAllowed.add("quotaTariffList");
|
||||
readOnlyUserRulesAllowed.add("quotaSummary");
|
||||
|
||||
for(String readOnlyUserRule : readOnlyUserRulesAllowed) {
|
||||
pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, 'ALLOW', ?) ON DUPLICATE KEY UPDATE rule=rule");
|
||||
pstmt.setLong(1, readOnlyUserRoleId);
|
||||
pstmt.setString(2, readOnlyUserRule);
|
||||
pstmt.setLong(3, readOnlyUserSortOrder++);
|
||||
pstmt.executeUpdate();
|
||||
}
|
||||
|
||||
pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, '*', 'DENY', ?) ON DUPLICATE KEY UPDATE rule=rule");
|
||||
pstmt.setLong(1, readOnlyUserRoleId);
|
||||
pstmt.setLong(2, readOnlyUserSortOrder);
|
||||
pstmt.executeUpdate();
|
||||
|
||||
if (rsRolePermissions != null && !rsRolePermissions.isClosed()) {
|
||||
rsRolePermissions.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (rs != null && !rs.isClosed()) {
|
||||
rs.close();
|
||||
}
|
||||
if (pstmt != null && !pstmt.isClosed()) {
|
||||
pstmt.close();
|
||||
}
|
||||
LOG.debug("Successfully added role permissions for new read-only user role");
|
||||
} catch (final SQLException e) {
|
||||
LOG.error("Exception while adding role permissions for read-only user role: " + e.getMessage());
|
||||
throw new CloudRuntimeException("Exception while adding role permissions for read-only user role: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addRolePermissionsForSupportAdmin(final Connection conn) {
|
||||
LOG.debug("Adding role permissions for new support admin role");
|
||||
try {
|
||||
PreparedStatement pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`roles` WHERE name = 'Support Admin - Default' AND is_default = 1");
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
long supportAdminRoleId = rs.getLong(1);
|
||||
int supportAdminSortOrder = 0;
|
||||
|
||||
pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`roles` WHERE name = 'Read-Only Admin - Default' AND is_default = 1");
|
||||
ResultSet rsReadOnlyAdmin = pstmt.executeQuery();
|
||||
if (rsReadOnlyAdmin.next()) {
|
||||
long readOnlyAdminRoleId = rsReadOnlyAdmin.getLong(1);
|
||||
pstmt = conn.prepareStatement("SELECT rule FROM `cloud`.`role_permissions` WHERE role_id = ? AND permission = 'ALLOW' ORDER BY sort_order");
|
||||
pstmt.setLong(1, readOnlyAdminRoleId);
|
||||
ResultSet rsRolePermissions = pstmt.executeQuery();
|
||||
|
||||
while (rsRolePermissions.next()) {
|
||||
String rule = rsRolePermissions.getString(1);
|
||||
pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, 'ALLOW', ?) ON DUPLICATE KEY UPDATE rule=rule");
|
||||
pstmt.setLong(1, supportAdminRoleId);
|
||||
pstmt.setString(2, rule);
|
||||
pstmt.setLong(3, supportAdminSortOrder++);
|
||||
pstmt.executeUpdate();
|
||||
}
|
||||
|
||||
List<String> supportAdminRulesAllowed = new ArrayList<>();
|
||||
supportAdminRulesAllowed.add("prepareHostForMaintenance");
|
||||
supportAdminRulesAllowed.add("cancelHostMaintenance");
|
||||
supportAdminRulesAllowed.add("enableStorageMaintenance");
|
||||
supportAdminRulesAllowed.add("cancelStorageMaintenance");
|
||||
supportAdminRulesAllowed.add("createServiceOffering");
|
||||
supportAdminRulesAllowed.add("createDiskOffering");
|
||||
supportAdminRulesAllowed.add("createNetworkOffering");
|
||||
supportAdminRulesAllowed.add("createVPCOffering");
|
||||
supportAdminRulesAllowed.add("startVirtualMachine");
|
||||
supportAdminRulesAllowed.add("stopVirtualMachine");
|
||||
supportAdminRulesAllowed.add("rebootVirtualMachine");
|
||||
supportAdminRulesAllowed.add("startKubernetesCluster");
|
||||
supportAdminRulesAllowed.add("stopKubernetesCluster");
|
||||
supportAdminRulesAllowed.add("createVolume");
|
||||
supportAdminRulesAllowed.add("attachVolume");
|
||||
supportAdminRulesAllowed.add("detachVolume");
|
||||
supportAdminRulesAllowed.add("uploadVolume");
|
||||
supportAdminRulesAllowed.add("attachIso");
|
||||
supportAdminRulesAllowed.add("detachIso");
|
||||
supportAdminRulesAllowed.add("registerTemplate");
|
||||
supportAdminRulesAllowed.add("registerIso");
|
||||
|
||||
for(String supportAdminRule : supportAdminRulesAllowed) {
|
||||
pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, 'ALLOW', ?) ON DUPLICATE KEY UPDATE rule=rule");
|
||||
pstmt.setLong(1, supportAdminRoleId);
|
||||
pstmt.setString(2, supportAdminRule);
|
||||
pstmt.setLong(3, supportAdminSortOrder++);
|
||||
pstmt.executeUpdate();
|
||||
}
|
||||
|
||||
pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, '*', 'DENY', ?) ON DUPLICATE KEY UPDATE rule=rule");
|
||||
pstmt.setLong(1, supportAdminRoleId);
|
||||
pstmt.setLong(2, supportAdminSortOrder);
|
||||
pstmt.executeUpdate();
|
||||
|
||||
if (rsRolePermissions != null && !rsRolePermissions.isClosed()) {
|
||||
rsRolePermissions.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (rsReadOnlyAdmin != null && !rsReadOnlyAdmin.isClosed()) {
|
||||
rsReadOnlyAdmin.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (rs != null && !rs.isClosed()) {
|
||||
rs.close();
|
||||
}
|
||||
if (pstmt != null && !pstmt.isClosed()) {
|
||||
pstmt.close();
|
||||
}
|
||||
LOG.debug("Successfully added role permissions for new support admin role");
|
||||
} catch (final SQLException e) {
|
||||
LOG.error("Exception while adding role permissions for support admin role: " + e.getMessage());
|
||||
throw new CloudRuntimeException("Exception while adding role permissions for support admin role: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addRolePermissionsForSupportUser(final Connection conn) {
|
||||
LOG.debug("Adding role permissions for new support user role");
|
||||
try {
|
||||
PreparedStatement pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`roles` WHERE name = 'Support User - Default' AND is_default = 1");
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
long supportUserRoleId = rs.getLong(1);
|
||||
int supportUserSortOrder = 0;
|
||||
|
||||
pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`roles` WHERE name = 'Read-Only User - Default' AND is_default = 1");
|
||||
ResultSet rsReadOnlyUser = pstmt.executeQuery();
|
||||
if (rsReadOnlyUser.next()) {
|
||||
long readOnlyUserRoleId = rsReadOnlyUser.getLong(1);
|
||||
pstmt = conn.prepareStatement("SELECT rule FROM `cloud`.`role_permissions` WHERE role_id = ? AND permission = 'ALLOW' ORDER BY sort_order");
|
||||
pstmt.setLong(1, readOnlyUserRoleId);
|
||||
ResultSet rsRolePermissions = pstmt.executeQuery();
|
||||
while (rsRolePermissions.next()) {
|
||||
String rule = rsRolePermissions.getString(1);
|
||||
pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, 'ALLOW', ?) ON DUPLICATE KEY UPDATE rule=rule");
|
||||
pstmt.setLong(1, supportUserRoleId);
|
||||
pstmt.setString(2, rule);
|
||||
pstmt.setLong(3, supportUserSortOrder++);
|
||||
pstmt.executeUpdate();
|
||||
}
|
||||
|
||||
List<String> supportUserRulesAllowed = new ArrayList<>();
|
||||
supportUserRulesAllowed.add("startVirtualMachine");
|
||||
supportUserRulesAllowed.add("stopVirtualMachine");
|
||||
supportUserRulesAllowed.add("rebootVirtualMachine");
|
||||
supportUserRulesAllowed.add("startKubernetesCluster");
|
||||
supportUserRulesAllowed.add("stopKubernetesCluster");
|
||||
supportUserRulesAllowed.add("createVolume");
|
||||
supportUserRulesAllowed.add("attachVolume");
|
||||
supportUserRulesAllowed.add("detachVolume");
|
||||
supportUserRulesAllowed.add("uploadVolume");
|
||||
supportUserRulesAllowed.add("attachIso");
|
||||
supportUserRulesAllowed.add("detachIso");
|
||||
supportUserRulesAllowed.add("registerTemplate");
|
||||
supportUserRulesAllowed.add("registerIso");
|
||||
supportUserRulesAllowed.add("getUploadParamsFor*");
|
||||
|
||||
for(String supportUserRule : supportUserRulesAllowed) {
|
||||
pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, ?, 'ALLOW', ?) ON DUPLICATE KEY UPDATE rule=rule");
|
||||
pstmt.setLong(1, supportUserRoleId);
|
||||
pstmt.setString(2, supportUserRule);
|
||||
pstmt.setLong(3, supportUserSortOrder++);
|
||||
pstmt.executeUpdate();
|
||||
}
|
||||
|
||||
pstmt = conn.prepareStatement("INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) VALUES (UUID(), ?, '*', 'DENY', ?) ON DUPLICATE KEY UPDATE rule=rule");
|
||||
pstmt.setLong(1, supportUserRoleId);
|
||||
pstmt.setLong(2, supportUserSortOrder);
|
||||
pstmt.executeUpdate();
|
||||
|
||||
if (rsRolePermissions != null && !rsRolePermissions.isClosed()) {
|
||||
rsRolePermissions.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (rsReadOnlyUser != null && !rsReadOnlyUser.isClosed()) {
|
||||
rsReadOnlyUser.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (rs != null && !rs.isClosed()) {
|
||||
rs.close();
|
||||
}
|
||||
if (pstmt != null && !pstmt.isClosed()) {
|
||||
pstmt.close();
|
||||
}
|
||||
LOG.debug("Successfully added role permissions for new support user role");
|
||||
} catch (final SQLException e) {
|
||||
LOG.error("Exception while adding role permissions for support user role: " + e.getMessage());
|
||||
throw new CloudRuntimeException("Exception while adding role permissions for support user role: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream[] getCleanupScripts() {
|
||||
final String scriptFile = "META-INF/db/schema-41400to41500-cleanup.sql";
|
||||
|
||||
@ -51,6 +51,9 @@ public class RoleVO implements Role {
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
@Column(name = "is_default")
|
||||
private boolean isDefault = false;
|
||||
|
||||
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||
private Date removed;
|
||||
|
||||
@ -75,6 +78,10 @@ public class RoleVO implements Role {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
@ -103,4 +110,8 @@ public class RoleVO implements Role {
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public boolean isDefault() {
|
||||
return isDefault;
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
package org.apache.cloudstack.acl.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.RoleVO;
|
||||
|
||||
@ -26,4 +27,6 @@ import java.util.List;
|
||||
public interface RoleDao extends GenericDao<RoleVO, Long> {
|
||||
List<RoleVO> findAllByName(String roleName);
|
||||
List<RoleVO> findAllByRoleType(RoleType type);
|
||||
List<RoleVO> findByName(String roleName);
|
||||
RoleVO findByNameAndType(String roleName, RoleType type);
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ package org.apache.cloudstack.acl.dao;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.RoleVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -30,6 +31,7 @@ import java.util.List;
|
||||
public class RoleDaoImpl extends GenericDaoBase<RoleVO, Long> implements RoleDao {
|
||||
private final SearchBuilder<RoleVO> RoleByNameSearch;
|
||||
private final SearchBuilder<RoleVO> RoleByTypeSearch;
|
||||
private final SearchBuilder<RoleVO> RoleByNameAndTypeSearch;
|
||||
|
||||
public RoleDaoImpl() {
|
||||
super();
|
||||
@ -41,6 +43,11 @@ public class RoleDaoImpl extends GenericDaoBase<RoleVO, Long> implements RoleDao
|
||||
RoleByTypeSearch = createSearchBuilder();
|
||||
RoleByTypeSearch.and("roleType", RoleByTypeSearch.entity().getRoleType(), 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.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -56,4 +63,19 @@ public class RoleDaoImpl extends GenericDaoBase<RoleVO, Long> implements RoleDao
|
||||
sc.setParameters("roleType", type);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoleVO> findByName(String roleName) {
|
||||
SearchCriteria<RoleVO> sc = RoleByNameSearch.create();
|
||||
sc.setParameters("roleName", roleName);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleVO findByNameAndType(String roleName, RoleType type) {
|
||||
SearchCriteria<RoleVO> sc = RoleByNameAndTypeSearch.create();
|
||||
sc.setParameters("roleName", roleName);
|
||||
sc.setParameters("roleType", type);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,4 +56,12 @@ public interface RolePermissionsDao extends GenericDao<RolePermissionVO, Long> {
|
||||
* @return returns list of role permissions
|
||||
*/
|
||||
List<RolePermissionVO> findAllByRoleIdSorted(Long roleId);
|
||||
|
||||
/**
|
||||
* Returns role permission for a given role and rule
|
||||
* @param roleId the ID of the role
|
||||
* @param roleId rule for the role
|
||||
* @return returns role permission
|
||||
*/
|
||||
RolePermissionVO findByRoleIdAndRule(Long roleId, String rule);
|
||||
}
|
||||
|
||||
@ -44,12 +44,18 @@ import java.util.Set;
|
||||
public class RolePermissionsDaoImpl extends GenericDaoBase<RolePermissionVO, Long> implements RolePermissionsDao {
|
||||
protected static final Logger LOGGER = Logger.getLogger(RolePermissionsDaoImpl.class);
|
||||
|
||||
private final SearchBuilder<RolePermissionVO> RolePermissionsSearchByRoleAndRule;
|
||||
private final SearchBuilder<RolePermissionVO> RolePermissionsSearch;
|
||||
private Attribute sortOrderAttribute;
|
||||
|
||||
public RolePermissionsDaoImpl() {
|
||||
super();
|
||||
|
||||
RolePermissionsSearchByRoleAndRule = createSearchBuilder();
|
||||
RolePermissionsSearchByRoleAndRule.and("roleId", RolePermissionsSearchByRoleAndRule.entity().getRoleId(), SearchCriteria.Op.EQ);
|
||||
RolePermissionsSearchByRoleAndRule.and("rule", RolePermissionsSearchByRoleAndRule.entity().getRule(), SearchCriteria.Op.EQ);
|
||||
RolePermissionsSearchByRoleAndRule.done();
|
||||
|
||||
RolePermissionsSearch = createSearchBuilder();
|
||||
RolePermissionsSearch.and("uuid", RolePermissionsSearch.entity().getUuid(), SearchCriteria.Op.EQ);
|
||||
RolePermissionsSearch.and("roleId", RolePermissionsSearch.entity().getRoleId(), SearchCriteria.Op.EQ);
|
||||
@ -174,4 +180,12 @@ public class RolePermissionsDaoImpl extends GenericDaoBase<RolePermissionVO, Lon
|
||||
}
|
||||
return rolePermissionList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolePermissionVO findByRoleIdAndRule(final Long roleId, final String rule) {
|
||||
final SearchCriteria<RolePermissionVO> sc = RolePermissionsSearchByRoleAndRule.create();
|
||||
sc.setParameters("roleId", roleId);
|
||||
sc.setParameters("rule", rule);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,3 +18,6 @@
|
||||
--;
|
||||
-- Schema upgrade cleanup from 4.14.0.0 to 4.15.0.0
|
||||
--;
|
||||
|
||||
-- remove the old NetApp storage APIs (unsupported since 4.12) from role_permissions
|
||||
DELETE from `cloud`.`role_permissions` WHERE rule IN ('createPool', 'modifyPool', 'deletePool', 'listPools', 'associateLun', 'dissociateLun', 'createLunOnFiler', 'destroyLunOnFiler', 'listLunsOnFiler', 'createVolumeOnFiler', 'destroyVolumeOnFiler', 'listVolumesOnFiler');
|
||||
|
||||
@ -18,3 +18,12 @@
|
||||
--;
|
||||
-- Schema upgrade from 4.14.0.0 to 4.15.0.0
|
||||
--;
|
||||
|
||||
ALTER TABLE `cloud`.`roles` ADD COLUMN `is_default` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'is this a default role';
|
||||
UPDATE `cloud`.`roles` SET `is_default` = 1 WHERE id IN (1, 2, 3, 4);
|
||||
|
||||
-- Updated Default CloudStack roles with read-only and support admin and user roles
|
||||
INSERT INTO `cloud`.`roles` (`uuid`, `name`, `role_type`, `description`, `is_default`) VALUES (UUID(), 'Read-Only Admin - Default', 'Admin', 'Default read-only admin role', 1);
|
||||
INSERT INTO `cloud`.`roles` (`uuid`, `name`, `role_type`, `description`, `is_default`) VALUES (UUID(), 'Read-Only User - Default', 'User', 'Default read-only user role', 1);
|
||||
INSERT INTO `cloud`.`roles` (`uuid`, `name`, `role_type`, `description`, `is_default`) VALUES (UUID(), 'Support Admin - Default', 'Admin', 'Default support admin role', 1);
|
||||
INSERT INTO `cloud`.`roles` (`uuid`, `name`, `role_type`, `description`, `is_default`) VALUES (UUID(), 'Support User - Default', 'User', 'Default support user role', 1);
|
||||
|
||||
@ -1204,6 +1204,17 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidApiName(String apiName) {
|
||||
if (apiName == null || apiName.isEmpty())
|
||||
return false;
|
||||
|
||||
if (!s_apiNameCmdClassMap.containsKey(apiName))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: rather than isError, we might was to pass in the status code to give more flexibility
|
||||
private void writeResponse(final HttpResponse resp, final String responseText, final int statusCode, final String responseType, final String reasonPhrase) {
|
||||
try {
|
||||
|
||||
@ -20,17 +20,20 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.acl.dao.RoleDao;
|
||||
import org.apache.cloudstack.acl.dao.RolePermissionsDao;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.command.admin.acl.CreateRoleCmd;
|
||||
import org.apache.cloudstack.api.command.admin.acl.CreateRolePermissionCmd;
|
||||
import org.apache.cloudstack.api.command.admin.acl.DeleteRoleCmd;
|
||||
import org.apache.cloudstack.api.command.admin.acl.DeleteRolePermissionCmd;
|
||||
import org.apache.cloudstack.api.command.admin.acl.ImportRoleCmd;
|
||||
import org.apache.cloudstack.api.command.admin.acl.ListRolePermissionsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.acl.ListRolesCmd;
|
||||
import org.apache.cloudstack.api.command.admin.acl.UpdateRoleCmd;
|
||||
@ -54,6 +57,7 @@ import com.cloud.utils.component.PluggableService;
|
||||
import com.cloud.utils.db.Transaction;
|
||||
import com.cloud.utils.db.TransactionCallback;
|
||||
import com.cloud.utils.db.TransactionStatus;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
public class RoleManagerImpl extends ManagerBase implements RoleService, Configurable, PluggableService {
|
||||
@ -124,11 +128,12 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolePermission findRolePermissionByUuid(final String uuid) {
|
||||
if (Strings.isNullOrEmpty(uuid)) {
|
||||
public RolePermission findRolePermissionByRoleIdAndRule(final Long roleId, final String rule) {
|
||||
if (roleId == null || Strings.isNullOrEmpty(rule)) {
|
||||
return null;
|
||||
}
|
||||
return rolePermissionsDao.findByUuid(uuid);
|
||||
|
||||
return rolePermissionsDao.findByRoleIdAndRule(roleId, rule);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -146,10 +151,99 @@ 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) {
|
||||
checkCallerAccess();
|
||||
return Transaction.execute(new TransactionCallback<RoleVO>() {
|
||||
@Override
|
||||
public RoleVO doInTransaction(TransactionStatus status) {
|
||||
RoleVO newRoleVO = roleDao.persist(new RoleVO(name, role.getRoleType(), description));
|
||||
if (newRoleVO == null) {
|
||||
throw new CloudRuntimeException("Unable to create the role: " + name + ", failed to persist in DB");
|
||||
}
|
||||
|
||||
List<RolePermissionVO> rolePermissions = rolePermissionsDao.findAllByRoleIdSorted(role.getId());
|
||||
if (rolePermissions != null && !rolePermissions.isEmpty()) {
|
||||
for (RolePermissionVO permission : rolePermissions) {
|
||||
rolePermissionsDao.persist(new RolePermissionVO(newRoleVO.getId(), permission.getRule().toString(), permission.getPermission(), permission.getDescription()));
|
||||
}
|
||||
}
|
||||
|
||||
return newRoleVO;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@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) {
|
||||
checkCallerAccess();
|
||||
if (Strings.isNullOrEmpty(name)) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role name provided");
|
||||
}
|
||||
if (type == null || type == RoleType.Unknown) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role type provided");
|
||||
}
|
||||
|
||||
List<RoleVO> existingRoles = roleDao.findByName(name);
|
||||
if (CollectionUtils.isNotEmpty(existingRoles) && !forced) {
|
||||
throw new CloudRuntimeException("Role already exists");
|
||||
}
|
||||
|
||||
return Transaction.execute(new TransactionCallback<RoleVO>() {
|
||||
@Override
|
||||
public RoleVO doInTransaction(TransactionStatus status) {
|
||||
RoleVO newRole = null;
|
||||
RoleVO existingRole = roleDao.findByNameAndType(name, type);
|
||||
if (existingRole != null) {
|
||||
if (existingRole.isDefault()) {
|
||||
throw new CloudRuntimeException("Failed to import the role: " + name + ", default role cannot be overriden");
|
||||
}
|
||||
|
||||
//Cleanup old role permissions
|
||||
List<? extends RolePermission> rolePermissions = rolePermissionsDao.findAllByRoleIdSorted(existingRole.getId());
|
||||
if (rolePermissions != null && !rolePermissions.isEmpty()) {
|
||||
for (RolePermission rolePermission : rolePermissions) {
|
||||
rolePermissionsDao.remove(rolePermission.getId());
|
||||
}
|
||||
}
|
||||
|
||||
existingRole.setName(name);
|
||||
existingRole.setRoleType(type);
|
||||
existingRole.setDescription(description);
|
||||
roleDao.update(existingRole.getId(), existingRole);
|
||||
|
||||
newRole = existingRole;
|
||||
} else {
|
||||
newRole = roleDao.persist(new RoleVO(name, type, description));
|
||||
}
|
||||
|
||||
if (newRole == null) {
|
||||
throw new CloudRuntimeException("Unable to import the role: " + name + ", failed to persist in DB");
|
||||
}
|
||||
|
||||
if (rules != null && !rules.isEmpty()) {
|
||||
for (Map<String, Object> ruleDetail : rules) {
|
||||
Rule rule = (Rule)ruleDetail.get(ApiConstants.RULE);
|
||||
RolePermission.Permission rulePermission = (RolePermission.Permission) ruleDetail.get(ApiConstants.PERMISSION);
|
||||
String ruleDescription = (String) ruleDetail.get(ApiConstants.DESCRIPTION);
|
||||
|
||||
rolePermissionsDao.persist(new RolePermissionVO(newRole.getId(), rule.toString(), rulePermission, ruleDescription));
|
||||
}
|
||||
}
|
||||
return newRole;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@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) {
|
||||
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");
|
||||
@ -159,9 +253,6 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
|
||||
roleVO.setName(name);
|
||||
}
|
||||
if (roleType != null) {
|
||||
if (role.getId() <= RoleType.User.getId()) {
|
||||
throw new PermissionDeniedException("The role type of default roles cannot be changed");
|
||||
}
|
||||
List<? extends Account> accounts = accountDao.findAccountsByRole(role.getId());
|
||||
if (accounts == null || accounts.isEmpty()) {
|
||||
roleVO.setRoleType(roleType);
|
||||
@ -184,7 +275,7 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
|
||||
if (role == null) {
|
||||
return false;
|
||||
}
|
||||
if (role.getId() <= RoleType.User.getId()) {
|
||||
if (role.isDefault()) {
|
||||
throw new PermissionDeniedException("Default roles cannot be deleted");
|
||||
}
|
||||
List<? extends Account> accounts = accountDao.findAccountsByRole(role.getId());
|
||||
@ -214,6 +305,14 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
|
||||
@ActionEvent(eventType = EventTypes.EVENT_ROLE_PERMISSION_CREATE, eventDescription = "creating Role Permission")
|
||||
public RolePermission createRolePermission(final Role role, final Rule rule, final RolePermission.Permission permission, final String description) {
|
||||
checkCallerAccess();
|
||||
if (role.isDefault()) {
|
||||
throw new PermissionDeniedException("Role permission cannot be added for Default roles");
|
||||
}
|
||||
|
||||
if (findRolePermissionByRoleIdAndRule(role.getId(), rule.toString()) != null) {
|
||||
throw new PermissionDeniedException("Rule already exists for the role: " + role.getName());
|
||||
}
|
||||
|
||||
return Transaction.execute(new TransactionCallback<RolePermissionVO>() {
|
||||
@Override
|
||||
public RolePermissionVO doInTransaction(TransactionStatus status) {
|
||||
@ -226,12 +325,18 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
|
||||
@ActionEvent(eventType = EventTypes.EVENT_ROLE_PERMISSION_UPDATE, eventDescription = "updating Role Permission order")
|
||||
public boolean updateRolePermission(final Role role, final List<RolePermission> newOrder) {
|
||||
checkCallerAccess();
|
||||
if (role.isDefault()) {
|
||||
throw new PermissionDeniedException("Role permission cannot be updated for Default roles");
|
||||
}
|
||||
return role != null && newOrder != null && rolePermissionsDao.update(role, newOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateRolePermission(Role role, RolePermission rolePermission, RolePermission.Permission permission) {
|
||||
checkCallerAccess();
|
||||
if (role.isDefault()) {
|
||||
throw new PermissionDeniedException("Role permission cannot be updated for Default roles");
|
||||
}
|
||||
return role != null && rolePermissionsDao.update(role, rolePermission, permission);
|
||||
}
|
||||
|
||||
@ -239,6 +344,10 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
|
||||
@ActionEvent(eventType = EventTypes.EVENT_ROLE_PERMISSION_DELETE, eventDescription = "deleting Role Permission")
|
||||
public boolean deleteRolePermission(final RolePermission rolePermission) {
|
||||
checkCallerAccess();
|
||||
Role role = findRole(rolePermission.getRoleId());
|
||||
if (role.isDefault()) {
|
||||
throw new PermissionDeniedException("Role permission cannot be deleted for Default roles");
|
||||
}
|
||||
return rolePermission != null && rolePermissionsDao.remove(rolePermission.getId());
|
||||
}
|
||||
|
||||
@ -277,7 +386,6 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
|
||||
rolesIterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -305,6 +413,18 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolePermission.Permission getRolePermission(String permission) {
|
||||
if (Strings.isNullOrEmpty(permission)) {
|
||||
return null;
|
||||
}
|
||||
if (!permission.equalsIgnoreCase(RolePermission.Permission.ALLOW.toString()) &&
|
||||
!permission.equalsIgnoreCase(RolePermission.Permission.DENY.toString())) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Values for permission parameter should be: allow or deny");
|
||||
}
|
||||
return permission.equalsIgnoreCase(RolePermission.Permission.ALLOW.toString()) ? RolePermission.Permission.ALLOW : RolePermission.Permission.DENY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfigComponentName() {
|
||||
return RoleService.class.getSimpleName();
|
||||
@ -319,6 +439,7 @@ public class RoleManagerImpl extends ManagerBase implements RoleService, Configu
|
||||
public List<Class<?>> getCommands() {
|
||||
final List<Class<?>> cmdList = new ArrayList<>();
|
||||
cmdList.add(CreateRoleCmd.class);
|
||||
cmdList.add(ImportRoleCmd.class);
|
||||
cmdList.add(ListRolesCmd.class);
|
||||
cmdList.add(UpdateRoleCmd.class);
|
||||
cmdList.add(DeleteRoleCmd.class);
|
||||
|
||||
@ -45,6 +45,14 @@ class TestData(object):
|
||||
"type": "User",
|
||||
"description": "Fake Role created by Marvin test"
|
||||
},
|
||||
"importrole": {
|
||||
"name": "MarvinFake Import Role ",
|
||||
"type": "User",
|
||||
"description": "Fake Import User Role created by Marvin test",
|
||||
"rules" : [{"rule":"list*", "permission":"allow","description":"Listing apis"},
|
||||
{"rule":"get*", "permission":"allow","description":"Get apis"},
|
||||
{"rule":"update*", "permission":"deny","description":"Update apis"}]
|
||||
},
|
||||
"roleadmin": {
|
||||
"name": "MarvinFake Admin Role ",
|
||||
"type": "Admin",
|
||||
@ -201,6 +209,91 @@ class TestDynamicRoles(cloudstackTestCase):
|
||||
msg="Role type does not match the test data"
|
||||
)
|
||||
|
||||
@attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
|
||||
def test_role_lifecycle_clone(self):
|
||||
"""
|
||||
Tests create role from existing role
|
||||
"""
|
||||
# Use self.role created in setUp()
|
||||
role_to_be_cloned = {
|
||||
"name": "MarvinFake Clone Role ",
|
||||
"roleid": self.role.id,
|
||||
"description": "Fake Role cloned by Marvin test"
|
||||
}
|
||||
|
||||
try:
|
||||
role_cloned = Role.create(
|
||||
self.apiclient,
|
||||
role_to_be_cloned
|
||||
)
|
||||
self.cleanup.append(role_cloned)
|
||||
except CloudstackAPIException as e:
|
||||
self.fail("Failed to create the role: %s" % e)
|
||||
|
||||
list_role_cloned= Role.list(self.apiclient, id=role_cloned.id)
|
||||
self.assertEqual(
|
||||
isinstance(list_role_cloned, list),
|
||||
True,
|
||||
"List Roles response was not a valid list"
|
||||
)
|
||||
self.assertEqual(
|
||||
len(list_role_cloned),
|
||||
1,
|
||||
"List Roles response size was not 1"
|
||||
)
|
||||
self.assertEqual(
|
||||
list_role_cloned[0].name,
|
||||
role_to_be_cloned["name"],
|
||||
msg="Role name does not match the test data"
|
||||
)
|
||||
self.assertEqual(
|
||||
list_role_cloned[0].type,
|
||||
self.testdata["role"]["type"],
|
||||
msg="Role type does not match the test data"
|
||||
)
|
||||
|
||||
list_rolepermissions = RolePermission.list(self.apiclient, roleid=self.role.id)
|
||||
self.validate_permissions_list(list_rolepermissions, role_cloned.id)
|
||||
|
||||
@attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
|
||||
def test_role_lifecycle_import(self):
|
||||
"""
|
||||
Tests import role with the rules
|
||||
"""
|
||||
# use importrole from testdata
|
||||
self.testdata["importrole"]["name"] += self.getRandomString()
|
||||
try:
|
||||
role_imported = Role.importRole(
|
||||
self.apiclient,
|
||||
self.testdata["importrole"]
|
||||
)
|
||||
self.cleanup.append(role_imported)
|
||||
except CloudstackAPIException as e:
|
||||
self.fail("Failed to import the role: %s" % e)
|
||||
|
||||
list_role_imported = Role.list(self.apiclient, id=role_imported.id)
|
||||
self.assertEqual(
|
||||
isinstance(list_role_imported, list),
|
||||
True,
|
||||
"List Roles response was not a valid list"
|
||||
)
|
||||
self.assertEqual(
|
||||
len(list_role_imported),
|
||||
1,
|
||||
"List Roles response size was not 1"
|
||||
)
|
||||
self.assertEqual(
|
||||
list_role_imported[0].name,
|
||||
self.testdata["importrole"]["name"],
|
||||
msg="Role name does not match the test data"
|
||||
)
|
||||
self.assertEqual(
|
||||
list_role_imported[0].type,
|
||||
self.testdata["importrole"]["type"],
|
||||
msg="Role type does not match the test data"
|
||||
)
|
||||
|
||||
self.validate_permissions_dict(self.testdata["importrole"]["rules"], role_imported.id)
|
||||
|
||||
@attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
|
||||
def test_role_lifecycle_update(self):
|
||||
@ -360,44 +453,23 @@ class TestDynamicRoles(cloudstackTestCase):
|
||||
self.cleanup.append(permission)
|
||||
permissions.append(permission)
|
||||
|
||||
|
||||
def validate_permissions_list(permissions):
|
||||
list_rolepermissions = RolePermission.list(self.apiclient, roleid=self.role.id)
|
||||
self.assertEqual(
|
||||
len(list_rolepermissions),
|
||||
len(permissions),
|
||||
msg="List of role permissions do not match created list of permissions"
|
||||
)
|
||||
|
||||
for idx, rolepermission in enumerate(list_rolepermissions):
|
||||
self.assertEqual(
|
||||
rolepermission.rule,
|
||||
permissions[idx].rule,
|
||||
msg="Rule permission don't match with expected item at the index"
|
||||
)
|
||||
self.assertEqual(
|
||||
rolepermission.permission,
|
||||
permissions[idx].permission,
|
||||
msg="Rule permission don't match with expected item at the index"
|
||||
)
|
||||
|
||||
# Move last item to the top
|
||||
rule = permissions.pop(len(permissions)-1)
|
||||
permissions = [rule] + permissions
|
||||
rule.update(self.apiclient, ruleorder=",".join(map(lambda x: x.id, permissions)))
|
||||
validate_permissions_list(permissions)
|
||||
self.validate_permissions_list(permissions, self.role.id)
|
||||
|
||||
# Move to the bottom
|
||||
rule = permissions.pop(0)
|
||||
permissions = permissions + [rule]
|
||||
rule.update(self.apiclient, ruleorder=",".join(map(lambda x: x.id, permissions)))
|
||||
validate_permissions_list(permissions)
|
||||
self.validate_permissions_list(permissions, self.role.id)
|
||||
|
||||
# Random shuffles
|
||||
for _ in range(3):
|
||||
shuffle(permissions)
|
||||
rule.update(self.apiclient, ruleorder=",".join(map(lambda x: x.id, permissions)))
|
||||
validate_permissions_list(permissions)
|
||||
self.validate_permissions_list(permissions, self.role.id)
|
||||
|
||||
@attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
|
||||
def test_rolepermission_lifecycle_update_permission(self):
|
||||
@ -580,3 +652,43 @@ class TestDynamicRoles(cloudstackTestCase):
|
||||
|
||||
# Perform actual API call for allow API
|
||||
self.checkApiCall(apiConfig, userApiClient)
|
||||
|
||||
def validate_permissions_list(self, permissions, roleid):
|
||||
list_rolepermissions = RolePermission.list(self.apiclient, roleid=roleid)
|
||||
self.assertEqual(
|
||||
len(list_rolepermissions),
|
||||
len(permissions),
|
||||
msg="List of role permissions do not match created list of permissions"
|
||||
)
|
||||
|
||||
for idx, rolepermission in enumerate(list_rolepermissions):
|
||||
self.assertEqual(
|
||||
rolepermission.rule,
|
||||
permissions[idx].rule,
|
||||
msg="Rule permission don't match with expected item at the index"
|
||||
)
|
||||
self.assertEqual(
|
||||
rolepermission.permission,
|
||||
permissions[idx].permission,
|
||||
msg="Rule permission don't match with expected item at the index"
|
||||
)
|
||||
|
||||
def validate_permissions_dict(self, permissions, roleid):
|
||||
list_rolepermissions = RolePermission.list(self.apiclient, roleid=roleid)
|
||||
self.assertEqual(
|
||||
len(list_rolepermissions),
|
||||
len(permissions),
|
||||
msg="List of role permissions do not match created list of permissions"
|
||||
)
|
||||
|
||||
for idx, rolepermission in enumerate(list_rolepermissions):
|
||||
self.assertEqual(
|
||||
rolepermission.rule,
|
||||
permissions[idx]["rule"],
|
||||
msg="Rule permission don't match with expected item at the index"
|
||||
)
|
||||
self.assertEqual(
|
||||
rolepermission.permission,
|
||||
permissions[idx]["permission"],
|
||||
msg="Rule permission don't match with expected item at the index"
|
||||
)
|
||||
|
||||
@ -102,12 +102,29 @@ class Role:
|
||||
"""Create role"""
|
||||
cmd = createRole.createRoleCmd()
|
||||
cmd.name = services["name"]
|
||||
if "type" in services:
|
||||
cmd.type = services["type"]
|
||||
if "roleid" in services:
|
||||
cmd.roleid = services["roleid"]
|
||||
if "description" in services:
|
||||
cmd.description = services["description"]
|
||||
|
||||
return Role(apiclient.createRole(cmd).__dict__)
|
||||
|
||||
@classmethod
|
||||
def importRole(cls, apiclient, services, domainid=None):
|
||||
"""Import role"""
|
||||
cmd = importRole.importRoleCmd()
|
||||
cmd.name = services["name"]
|
||||
cmd.type = services["type"]
|
||||
cmd.rules = services["rules"]
|
||||
if "description" in services:
|
||||
cmd.description = services["description"]
|
||||
if "forced" in services:
|
||||
cmd.type = services["forced"]
|
||||
|
||||
return Role(apiclient.importRole(cmd).__dict__)
|
||||
|
||||
def delete(self, apiclient):
|
||||
"""Delete Role"""
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user