diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 5b7bf5914b3..076a7c59211 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -453,6 +453,8 @@ public class EventTypes { public static final String EVENT_ACL_GROUP_UPDATE = "ACLGROUP.UPDATE"; public static final String EVENT_ACL_GROUP_CREATE = "ACLGROUP.CREATE"; public static final String EVENT_ACL_GROUP_DELETE = "ACLGROUP.DELETE"; + public static final String EVENT_ACL_GROUP_GRANT = "ACLGROUP.GRANT"; + public static final String EVENT_ACL_GROUP_REVOKE = "ACLGROUP.REVOKE"; static { diff --git a/api/src/org/apache/cloudstack/acl/AclService.java b/api/src/org/apache/cloudstack/acl/AclService.java index 1f4d620f96f..e01c3b6afa3 100644 --- a/api/src/org/apache/cloudstack/acl/AclService.java +++ b/api/src/org/apache/cloudstack/acl/AclService.java @@ -18,6 +18,8 @@ package org.apache.cloudstack.acl; import java.util.List; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; + public interface AclService { /** @@ -50,6 +52,10 @@ public interface AclService { AclGroup removeAccountsFromGroup(List acctIds, Long groupId); + AclGroup grantEntityPermissionToAclGroup(long aclGroupId, String entityType, long entityId, AccessType accessType); + + AclGroup revokeEntityPermissionFromAclGroup(long aclGroupId, String entityType, long entityId, AccessType accessType); + /** * Creates an acl group for the given domain. * diff --git a/api/src/org/apache/cloudstack/acl/SecurityChecker.java b/api/src/org/apache/cloudstack/acl/SecurityChecker.java index 3a721fe7fcb..9943f6b2c59 100644 --- a/api/src/org/apache/cloudstack/acl/SecurityChecker.java +++ b/api/src/org/apache/cloudstack/acl/SecurityChecker.java @@ -34,7 +34,9 @@ public interface SecurityChecker extends Adapter { ListEntry, ModifyEntry, ModifyProject, - UseNetwork + UseNetwork, + DeleteEntry, + OperationOnEntry } /** diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index cd8479ff5db..78200e58b50 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -525,6 +525,10 @@ public class ApiConstants { public static final String ACL_ROLE_IDS = "roleids"; public static final String ACL_APIS = "apis"; public static final String ACL_GROUPS = "groups"; + public static final String ACL_PERMISSIONS = "permission"; + public static final String ENTITY_TYPE = "entitytype"; + public static final String ENTITY_ID = "entityid"; + public static final String ACCESS_TYPE = "accesstype"; public enum HostDetails { all, capacity, events, stats, min; diff --git a/api/src/org/apache/cloudstack/api/command/admin/acl/GrantPermissionToAclGroupCmd.java b/api/src/org/apache/cloudstack/api/command/admin/acl/GrantPermissionToAclGroupCmd.java new file mode 100644 index 00000000000..9fefa75e4b0 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/acl/GrantPermissionToAclGroupCmd.java @@ -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.admin.acl; + + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.acl.AclGroup; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.AclGroupResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; + + +@APICommand(name = "grantPermissionToAclGroup", description = "grant entity permission to an acl group", responseObject = AclGroupResponse.class) +public class GrantPermissionToAclGroupCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(GrantPermissionToAclGroupCmd.class.getName()); + private static final String s_name = "grantpermissiontoaclgroupresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @ACL + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AclGroupResponse.class, + required = true, description = "The ID of the acl group") + private Long id; + + @Parameter(name = ApiConstants.ENTITY_TYPE, type = CommandType.STRING, required = true, description = "entity class simple name.") + private String entityType; + + @Parameter(name = ApiConstants.ENTITY_ID, type = CommandType.UUID, required = true, description = "The ID of the entity") + private Long entityId; + + @Parameter(name = ApiConstants.ACCESS_TYPE, type = CommandType.STRING, required = true, description = "access type for the entity") + private String accessType; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getId() { + return id; + } + + public String getEntityType() { + return entityType; + } + + public Long getEntityId() { + return entityId; + } + + public AccessType getAccessType() { + return AccessType.valueOf(accessType); + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException { + CallContext.current().setEventDetails("Acl group Id: " + getId()); + AclGroup result = _aclService.grantEntityPermissionToAclGroup(id, entityType, entityId, getAccessType()); + if (result != null){ + AclGroupResponse response = _responseGenerator.createAclGroupResponse(result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to grant permission to acl group"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ACL_GROUP_GRANT; + } + + @Override + public String getEventDescription() { + return "granting permission to acl group"; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.AclGroup; + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/acl/RevokePermissionFromAclGroupCmd.java b/api/src/org/apache/cloudstack/api/command/admin/acl/RevokePermissionFromAclGroupCmd.java new file mode 100644 index 00000000000..b9a75d732eb --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/acl/RevokePermissionFromAclGroupCmd.java @@ -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.admin.acl; + + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.acl.AclGroup; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.AclGroupResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; + + +@APICommand(name = "revokePermissionFromAclGroup", description = "revoke entity permission from an acl group", responseObject = AclGroupResponse.class) +public class RevokePermissionFromAclGroupCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(RevokePermissionFromAclGroupCmd.class.getName()); + private static final String s_name = "revokepermissiontoaclgroupresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @ACL + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AclGroupResponse.class, + required = true, description = "The ID of the acl group") + private Long id; + + @Parameter(name = ApiConstants.ENTITY_TYPE, type = CommandType.STRING, required = true, description = "entity class simple name.") + private String entityType; + + @Parameter(name = ApiConstants.ENTITY_ID, type = CommandType.UUID, required = true, description = "The ID of the entity") + private Long entityId; + + @Parameter(name = ApiConstants.ACCESS_TYPE, type = CommandType.STRING, required = true, description = "access type for the entity") + private String accessType; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getId() { + return id; + } + + public String getEntityType() { + return entityType; + } + + public Long getEntityId() { + return entityId; + } + + public AccessType getAccessType() { + return AccessType.valueOf(accessType); + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException { + CallContext.current().setEventDetails("Acl group Id: " + getId()); + AclGroup result = _aclService.revokeEntityPermissionFromAclGroup(id, entityType, entityId, getAccessType()); + if (result != null){ + AclGroupResponse response = _responseGenerator.createAclGroupResponse(result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to grant permission to acl group"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ACL_GROUP_GRANT; + } + + @Override + public String getEventDescription() { + return "granting permission to acl group"; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.AclGroup; + } + +} diff --git a/api/src/org/apache/cloudstack/api/response/AclEntityPermissionResponse.java b/api/src/org/apache/cloudstack/api/response/AclEntityPermissionResponse.java new file mode 100644 index 00000000000..da3b4b20899 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/AclEntityPermissionResponse.java @@ -0,0 +1,112 @@ +// 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.response; + +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; + +public class AclEntityPermissionResponse extends BaseResponse { + + @SerializedName(ApiConstants.GROUP_ID) + @Param(description = "the ID of the acl group") + private String groupId; + + @SerializedName(ApiConstants.ENTITY_TYPE) + @Param(description = "the entity type of this permission") + private String entityType; + + @SerializedName(ApiConstants.ENTITY_ID) + @Param(description = "the uuid of the entity involved in this permission") + private String entityId; + + @SerializedName(ApiConstants.ACCESS_TYPE) + @Param(description = "access type involved in this permission") + private String accessType; + + + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getEntityType() { + return entityType; + } + + public void setEntityType(String entityType) { + this.entityType = entityType; + } + + public String getEntityId() { + return entityId; + } + + public void setEntityId(String entityId) { + this.entityId = entityId; + } + + public String getAccessType() { + return accessType; + } + + public void setAccessType(String accessType) { + this.accessType = accessType; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((entityType == null) ? 0 : entityType.hashCode()); + result = prime * result + ((entityId == null) ? 0 : entityId.hashCode()); + result = prime * result + ((accessType == null) ? 0 : accessType.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AclEntityPermissionResponse other = (AclEntityPermissionResponse) obj; + if (entityType == null) { + if (other.entityType != null) + return false; + } else if (!entityType.equals(other.entityType)) { + return false; + } else if ((entityId == null && other.entityId != null) || !entityId.equals(other.entityId)) { + return false; + } else if ((accessType == null && other.accessType != null) || !accessType.equals(other.accessType)) { + return false; + } + return true; + } + + + +} diff --git a/api/src/org/apache/cloudstack/api/response/AclGroupResponse.java b/api/src/org/apache/cloudstack/api/response/AclGroupResponse.java index 94fe8b5df39..e5315043be0 100644 --- a/api/src/org/apache/cloudstack/api/response/AclGroupResponse.java +++ b/api/src/org/apache/cloudstack/api/response/AclGroupResponse.java @@ -60,9 +60,14 @@ public class AclGroupResponse extends BaseResponse { @Param(description = "acl roles granted to this acl group ") private Set roleList; + @SerializedName(ApiConstants.ACL_PERMISSIONS) + @Param(description = "permissions granted to this acl group ") + private Set permList; + public AclGroupResponse() { accountIdList = new LinkedHashSet(); roleList = new LinkedHashSet(); + permList = new LinkedHashSet(); } @Override @@ -116,6 +121,18 @@ public class AclGroupResponse extends BaseResponse { return roleList; } + public Set getPermissionList() { + return permList; + } + + public void setPermissionList(Set perms) { + permList = perms; + } + + public void addPermission(AclEntityPermissionResponse perm) { + permList.add(perm); + } + @Override public int hashCode() { final int prime = 31; diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 8aad3a717bb..9bb0ea25a55 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -692,4 +692,6 @@ addAclRoleToAclGroup=7 removeAclRoleFromAclGroup=7 addAccountToAclGroup=7 removeAccountFromAclGroup=7 +grantPermissionToAclGroup=7 +revokePermissionFromAclGroup=7 diff --git a/engine/schema/src/org/apache/cloudstack/acl/AclEntityPermissionVO.java b/engine/schema/src/org/apache/cloudstack/acl/AclEntityPermissionVO.java index b9e9174f6cf..a3945163221 100644 --- a/engine/schema/src/org/apache/cloudstack/acl/AclEntityPermissionVO.java +++ b/engine/schema/src/org/apache/cloudstack/acl/AclEntityPermissionVO.java @@ -31,7 +31,10 @@ public class AclEntityPermissionVO implements AclEntityPermission { private String entityType; @Column(name = "entity_id") - private Long entityId; + private long entityId; + + @Column(name = "entity_uuid") + private String entityUuid; @Column(name = "access_type") @Enumerated(value = EnumType.STRING) @@ -43,6 +46,18 @@ public class AclEntityPermissionVO implements AclEntityPermission { @Column(name = GenericDao.CREATED_COLUMN) private Date created; + public AclEntityPermissionVO() { + + } + + public AclEntityPermissionVO(long groupId, String entityType, long entityId, String entityUuid, AccessType atype) { + aclGroupId = groupId; + this.entityType = entityType; + this.entityId = entityId; + this.entityUuid = entityUuid; + accessType = atype; + } + @Override public long getId() { return id; @@ -63,11 +78,36 @@ public class AclEntityPermissionVO implements AclEntityPermission { return entityId; } + public String getEntityUuid() { + return entityUuid; + } + @Override public AccessType getAccessType() { return accessType; } + + public void setAclGroupId(long aclGroupId) { + this.aclGroupId = aclGroupId; + } + + public void setEntityType(String entityType) { + this.entityType = entityType; + } + + public void setEntityId(long entityId) { + this.entityId = entityId; + } + + public void setEntityUuid(String entityUuid) { + this.entityUuid = entityUuid; + } + + public void setAccessType(AccessType accessType) { + this.accessType = accessType; + } + public Date getRemoved() { return removed; } diff --git a/engine/schema/src/org/apache/cloudstack/acl/dao/AclEntityPermissionDaoImpl.java b/engine/schema/src/org/apache/cloudstack/acl/dao/AclEntityPermissionDaoImpl.java index 71c6e64118d..482c9f39228 100644 --- a/engine/schema/src/org/apache/cloudstack/acl/dao/AclEntityPermissionDaoImpl.java +++ b/engine/schema/src/org/apache/cloudstack/acl/dao/AclEntityPermissionDaoImpl.java @@ -33,6 +33,11 @@ import com.cloud.utils.db.SearchCriteria; public class AclEntityPermissionDaoImpl extends GenericDaoBase implements AclEntityPermissionDao { private SearchBuilder findByGroupEntity; + public AclEntityPermissionDaoImpl() + { + + } + @Override public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index c4ab6b86a22..b19d6efcfbd 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -151,6 +151,7 @@ import org.apache.cloudstack.usage.UsageTypes; import com.cloud.api.query.ViewResponseHelper; import com.cloud.api.query.vo.AccountJoinVO; +import com.cloud.api.query.vo.AclGroupJoinVO; import com.cloud.api.query.vo.AclRoleJoinVO; import com.cloud.api.query.vo.AsyncJobJoinVO; import com.cloud.api.query.vo.ControlledViewEntity; @@ -3683,19 +3684,10 @@ public class ApiResponseHelper implements ResponseGenerator { @Override public AclGroupResponse createAclGroupResponse(AclGroup group) { - AclGroupResponse response = new AclGroupResponse(); - - response.setId(group.getUuid()); - response.setName(group.getName()); - response.setDescription(group.getDescription()); - Domain domain = _entityMgr.findById(Domain.class, group.getDomainId()); - if (domain != null) { - response.setDomainId(domain.getUuid()); - response.setDomainName(domain.getName()); - } - - response.setObjectName("aclgroup"); - return response; + List viewGroups = ApiDBUtils.newAclGroupView(group); + List listGroups = ViewResponseHelper.createAclGroupResponses(viewGroups); + assert listGroups != null && listGroups.size() == 1 : "There should be one acl role returned"; + return listGroups.get(0); } diff --git a/server/src/com/cloud/api/query/dao/AclGroupJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/AclGroupJoinDaoImpl.java index 9b70542936c..a1ffad29e02 100644 --- a/server/src/com/cloud/api/query/dao/AclGroupJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/AclGroupJoinDaoImpl.java @@ -30,6 +30,7 @@ import org.springframework.stereotype.Component; import org.apache.cloudstack.acl.AclGroup; import org.apache.cloudstack.acl.AclGroupAccountMapVO; import org.apache.cloudstack.acl.dao.AclGroupAccountMapDao; +import org.apache.cloudstack.api.response.AclEntityPermissionResponse; import org.apache.cloudstack.api.response.AclGroupResponse; import org.apache.cloudstack.api.response.AclRoleResponse; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; @@ -88,6 +89,13 @@ public class AclGroupJoinDaoImpl extends GenericDaoBase im roleResp.setName(group.getRoleName()); response.addRole(roleResp); } + if (group.getEntityId() > 0) { + AclEntityPermissionResponse permResp = new AclEntityPermissionResponse(); + permResp.setEntityId(group.getEntityUuid()); + permResp.setEntityType(group.getEntityType()); + permResp.setAccessType(group.getAccessType().toString()); + response.addPermission(permResp); + } response.setObjectName("aclgroup"); @@ -105,6 +113,13 @@ public class AclGroupJoinDaoImpl extends GenericDaoBase im roleResp.setName(group.getRoleName()); response.addRole(roleResp); } + if (group.getEntityId() > 0) { + AclEntityPermissionResponse permResp = new AclEntityPermissionResponse(); + permResp.setEntityId(group.getEntityUuid()); + permResp.setEntityType(group.getEntityType()); + permResp.setAccessType(group.getAccessType().toString()); + response.addPermission(permResp); + } return response; } diff --git a/server/src/com/cloud/api/query/vo/AclGroupJoinVO.java b/server/src/com/cloud/api/query/vo/AclGroupJoinVO.java index dd8e20e9e19..0a32f4d16ba 100644 --- a/server/src/com/cloud/api/query/vo/AclGroupJoinVO.java +++ b/server/src/com/cloud/api/query/vo/AclGroupJoinVO.java @@ -20,11 +20,15 @@ import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; + import com.cloud.utils.db.GenericDao; @Entity @@ -80,6 +84,19 @@ public class AclGroupJoinVO extends BaseViewVO { @Column(name = "account_name") private String accountName; + @Column(name = "entity_type") + private String entityType; + + @Column(name = "entity_id") + private long entityId; + + @Column(name = "entity_uuid") + private String entityUuid; + + @Column(name = "access_type") + @Enumerated(value = EnumType.STRING) + AccessType accessType; + public AclGroupJoinVO() { } @@ -154,5 +171,20 @@ public class AclGroupJoinVO extends BaseViewVO { return accountName; } + public String getEntityType() { + return entityType; + } + + public long getEntityId() { + return entityId; + } + + public String getEntityUuid() { + return entityUuid; + } + + public AccessType getAccessType() { + return accessType; + } } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 3a4fd31f9ef..ae06643c451 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -63,11 +63,13 @@ import org.apache.cloudstack.api.command.admin.acl.CreateAclGroupCmd; import org.apache.cloudstack.api.command.admin.acl.CreateAclRoleCmd; import org.apache.cloudstack.api.command.admin.acl.DeleteAclGroupCmd; import org.apache.cloudstack.api.command.admin.acl.DeleteAclRoleCmd; +import org.apache.cloudstack.api.command.admin.acl.GrantPermissionToAclGroupCmd; import org.apache.cloudstack.api.command.admin.acl.GrantPermissionToAclRoleCmd; import org.apache.cloudstack.api.command.admin.acl.ListAclGroupsCmd; import org.apache.cloudstack.api.command.admin.acl.ListAclRolesCmd; import org.apache.cloudstack.api.command.admin.acl.RemoveAccountFromAclGroupCmd; import org.apache.cloudstack.api.command.admin.acl.RemoveAclRoleFromAclGroupCmd; +import org.apache.cloudstack.api.command.admin.acl.RevokePermissionFromAclGroupCmd; import org.apache.cloudstack.api.command.admin.acl.RevokePermissionFromAclRoleCmd; import org.apache.cloudstack.api.command.admin.autoscale.CreateCounterCmd; import org.apache.cloudstack.api.command.admin.autoscale.DeleteCounterCmd; @@ -2877,6 +2879,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(ListAclGroupsCmd.class); cmdList.add(AddAccountToAclGroupCmd.class); cmdList.add(RemoveAccountFromAclGroupCmd.class); + cmdList.add(GrantPermissionToAclGroupCmd.class); + cmdList.add(RevokePermissionFromAclGroupCmd.class); return cmdList; } diff --git a/server/src/org/apache/cloudstack/acl/AclServiceImpl.java b/server/src/org/apache/cloudstack/acl/AclServiceImpl.java index 00d15dbf458..1e5ad158920 100644 --- a/server/src/org/apache/cloudstack/acl/AclServiceImpl.java +++ b/server/src/org/apache/cloudstack/acl/AclServiceImpl.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.acl; +import java.util.HashMap; import java.util.List; import javax.ejb.Local; @@ -23,24 +24,31 @@ import javax.inject.Inject; import org.apache.log4j.Logger; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.acl.dao.AclApiPermissionDao; import org.apache.cloudstack.acl.dao.AclEntityPermissionDao; import org.apache.cloudstack.acl.dao.AclGroupAccountMapDao; import org.apache.cloudstack.acl.dao.AclGroupDao; import org.apache.cloudstack.acl.dao.AclGroupRoleMapDao; import org.apache.cloudstack.acl.dao.AclRoleDao; +import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.context.CallContext; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; +import com.cloud.storage.Snapshot; +import com.cloud.storage.Volume; +import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.dao.AccountDao; +import com.cloud.uservm.UserVm; import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; +import com.cloud.utils.db.EntityManager; import com.cloud.utils.db.Transaction; @Local(value = {AclService.class}) @@ -61,6 +69,9 @@ public class AclServiceImpl extends ManagerBase implements AclService, Manager { @Inject AclGroupDao _aclGroupDao; + @Inject + EntityManager _entityMgr; + @Inject AclGroupRoleMapDao _aclGroupRoleMapDao; @@ -73,6 +84,15 @@ public class AclServiceImpl extends ManagerBase implements AclService, Manager { @Inject AclEntityPermissionDao _entityPermissionDao; + public static HashMap entityClassMap = new HashMap(); + + static { + entityClassMap.put("VirtualMachine", UserVm.class); + entityClassMap.put("Volume", Volume.class); + entityClassMap.put("Template", VirtualMachineTemplate.class); + entityClassMap.put("Snapshot", Snapshot.class); + // To be filled in later depending on the entity permission grant scope + } @DB @Override @@ -188,7 +208,7 @@ public class AclServiceImpl extends ManagerBase implements AclService, Manager { Transaction txn = Transaction.currentTxn(); txn.start(); - // add entries in acl_api_permission table + // remove entries from acl_api_permission table for (String api : apiNames) { AclApiPermissionVO perm = _apiPermissionDao.findByRoleAndApi(aclRoleId, api); if (perm != null) { @@ -200,6 +220,80 @@ public class AclServiceImpl extends ManagerBase implements AclService, Manager { return role; } + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_ACL_GROUP_GRANT, eventDescription = "Granting entity permission to Acl Group") + public AclGroup grantEntityPermissionToAclGroup(long aclGroupId, String entityType, long entityId, AccessType accessType) { + Account caller = CallContext.current().getCallingAccount(); + // get the Acl Group entity + AclGroup group = _aclGroupDao.findById(aclGroupId); + if (group == null) { + throw new InvalidParameterValueException("Unable to find acl group: " + aclGroupId + + "; failed to grant permission to group."); + } + // check permissions + _accountMgr.checkAccess(caller, null, true, group); + + // get the entity and check permission + Class entityClass = entityClassMap.get(entityType); + if (entityClass == null) { + throw new InvalidParameterValueException("Entity type " + entityType + " permission granting is not supported yet"); + } + ControlledEntity entity = (ControlledEntity)_entityMgr.findById(entityClass, entityId); + if (entity == null) { + throw new InvalidParameterValueException("Unable to find entity " + entityType + " by id: " + entityId); + } + _accountMgr.checkAccess(caller,null, true, entity); + + // add entry in acl_entity_permission table + AclEntityPermissionVO perm = _entityPermissionDao.findByGroupAndEntity(aclGroupId, entityType, entityId, accessType); + if (perm == null) { + // not there already + String entityUuid = String.valueOf(entityId); + if (entity instanceof Identity) { + entityUuid = ((Identity)entity).getUuid(); + } + perm = new AclEntityPermissionVO(aclGroupId, entityType, entityId, entityUuid, accessType); + _entityPermissionDao.persist(perm); + } + return group; + + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_ACL_GROUP_REVOKE, eventDescription = "Revoking entity permission from Acl Group") + public AclGroup revokeEntityPermissionFromAclGroup(long aclGroupId, String entityType, long entityId, AccessType accessType) { + Account caller = CallContext.current().getCallingAccount(); + // get the Acl Group entity + AclGroup group = _aclGroupDao.findById(aclGroupId); + if (group == null) { + throw new InvalidParameterValueException("Unable to find acl group: " + aclGroupId + + "; failed to revoke permission from group."); + } + // check permissions + _accountMgr.checkAccess(caller, null, true, group); + + // get the entity and check permission + Class entityClass = entityClassMap.get(entityType); + if (entityClass == null) { + throw new InvalidParameterValueException("Entity type " + entityType + " permission revoke is not supported yet"); + } + ControlledEntity entity = (ControlledEntity)_entityMgr.findById(entityClass, entityId); + if (entity == null) { + throw new InvalidParameterValueException("Unable to find entity " + entityType + " by id: " + entityId); + } + _accountMgr.checkAccess(caller, null, true, entity); + + // remove entry from acl_entity_permission table + AclEntityPermissionVO perm = _entityPermissionDao.findByGroupAndEntity(aclGroupId, entityType, entityId, accessType); + if (perm != null) { + // not removed yet + _entityPermissionDao.remove(perm.getId()); + } + return group; + } + @DB @Override @ActionEvent(eventType = EventTypes.EVENT_ACL_GROUP_UPDATE, eventDescription = "Adding roles to acl group") @@ -326,7 +420,7 @@ public class AclServiceImpl extends ManagerBase implements AclService, Manager { Transaction txn = Transaction.currentTxn(); txn.start(); - // add entries in acl_group_account_map table + // remove entries from acl_group_account_map table for (Long acctId : acctIds) { // check account permissions Account account = _accountDao.findById(acctId); diff --git a/setup/db/db/schema-420to430.sql b/setup/db/db/schema-420to430.sql index 78c2afe0b94..c23c4980873 100644 --- a/setup/db/db/schema-420to430.sql +++ b/setup/db/db/schema-420to430.sql @@ -361,6 +361,7 @@ CREATE TABLE `cloud`.`acl_entity_permission` ( `group_id` bigint unsigned NOT NULL, `entity_type` varchar(100) NOT NULL, `entity_id` bigint unsigned NOT NULL, + `entity_uuid` varchar(40), `access_type` varchar(40) NOT NULL, `removed` datetime COMMENT 'date the permission was revoked', `created` datetime COMMENT 'date the permission was granted', @@ -414,7 +415,11 @@ CREATE VIEW `cloud`.`acl_group_view` AS acl_role.name role_name, account.id account_id, account.uuid account_uuid, - account.account_name account_name + account.account_name account_name, + acl_entity_permission.entity_id entity_id, + acl_entity_permission.entity_uuid entity_uuid, + acl_entity_permission.entity_type entity_type, + acl_entity_permission.access_type access_type from `cloud`.`acl_group` inner join @@ -426,5 +431,7 @@ CREATE VIEW `cloud`.`acl_group_view` AS left join `cloud`.`acl_group_account_map` ON acl_group.id = acl_group_account_map.group_id left join - `cloud`.`account` ON acl_group_account_map.account_id = account.id; + `cloud`.`account` ON acl_group_account_map.account_id = account.id + left join + `cloud`.`acl_entity_permission` ON acl_group.id = acl_entity_permission.group_id; \ No newline at end of file