mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-11-04 00:02:37 +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"]
 | 
			
		||||
        cmd.type = services["type"]
 | 
			
		||||
        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