diff --git a/services/iam/plugin/pom.xml b/services/iam/plugin/pom.xml new file mode 100644 index 00000000000..0650e4311a8 --- /dev/null +++ b/services/iam/plugin/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + cloud-plugin-iam + Apache CloudStack IAM - Plugin + + org.apache.cloudstack + cloudstack-service-iam + 4.4.0-SNAPSHOT + ../pom.xml + + + + org.apache.cloudstack + cloud-api + ${project.version} + + + org.apache.cloudstack + cloud-engine-schema + ${project.version} + + + org.apache.cloudstack + cloud-server + ${project.version} + + + org.apache.cloudstack + cloud-iam + ${project.version} + + + org.apache.cloudstack + cloud-api + ${project.version} + test-jar + test + + + diff --git a/services/iam/plugin/resources/META-INF/cloudstack/iam-access-checkers/module.properties b/services/iam/plugin/resources/META-INF/cloudstack/iam-access-checkers/module.properties new file mode 100644 index 00000000000..c87480d47b5 --- /dev/null +++ b/services/iam/plugin/resources/META-INF/cloudstack/iam-access-checkers/module.properties @@ -0,0 +1,18 @@ +# 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. +name=iam-access-checkers +parent=api \ No newline at end of file diff --git a/services/iam/plugin/resources/META-INF/cloudstack/iam-access-checkers/spring-iam-access-checkers-context.xml b/services/iam/plugin/resources/META-INF/cloudstack/iam-access-checkers/spring-iam-access-checkers-context.xml new file mode 100644 index 00000000000..983bf08b1d6 --- /dev/null +++ b/services/iam/plugin/resources/META-INF/cloudstack/iam-access-checkers/spring-iam-access-checkers-context.xml @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/AddAccountToIAMGroupCmd.java b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/AddAccountToIAMGroupCmd.java new file mode 100644 index 00000000000..bea3fc97d10 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/AddAccountToIAMGroupCmd.java @@ -0,0 +1,127 @@ +// 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.iam; + +import java.util.List; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.iam.IAMApiService; +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.AccountResponse; +import org.apache.cloudstack.api.response.iam.IAMGroupResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.iam.api.IAMGroup; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; + + +@APICommand(name = "addAccountToIAMGroup", description = "add account to an iam group", responseObject = IAMGroupResponse.class) +public class AddAccountToIAMGroupCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(AddAccountToIAMGroupCmd.class.getName()); + private static final String s_name = "addaccounttoiamgroupresponse"; + + @Inject + public IAMApiService _iamApiSrv; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @ACL + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IAMGroupResponse.class, + required = true, description = "The ID of the iam group") + private Long id; + + @ACL + @Parameter(name = ApiConstants.ACCOUNTS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AccountResponse.class, description = "comma separated list of account id that are going to be assigned to the iam group.") + private List accountIdList; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getId() { + return id; + } + + + public List getAccountIdList() { + return accountIdList; + } + + ///////////////////////////////////////////////////// + /////////////// 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("IAM group Id: " + getId()); + IAMGroup result = _iamApiSrv.addAccountsToGroup(accountIdList, id); + if (result != null){ + IAMGroupResponse response = _iamApiSrv.createIAMGroupResponse(result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add accounts to iam group"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IAM_GROUP_UPDATE; + } + + @Override + public String getEventDescription() { + return "adding accounts to iam group"; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.IAMGroup; + } + +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/AddIAMPermissionToIAMPolicyCmd.java b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/AddIAMPermissionToIAMPolicyCmd.java new file mode 100644 index 00000000000..e9915378291 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/AddIAMPermissionToIAMPolicyCmd.java @@ -0,0 +1,155 @@ +// 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.iam; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.acl.PermissionScope; +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.iam.IAMPolicyResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.iam.IAMApiService; +import org.apache.cloudstack.iam.api.IAMPolicy; +import org.apache.cloudstack.iam.api.IAMPolicyPermission.Permission; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.utils.db.EntityManager; + + +@APICommand(name = "addIAMPermissionToIAMPolicy", description = "Add IAM permission to an iam policy", responseObject = IAMPolicyResponse.class) +public class AddIAMPermissionToIAMPolicyCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(AddIAMPermissionToIAMPolicyCmd.class.getName()); + private static final String s_name = "addiampermissiontoiampolicyresponse"; + + @Inject + public IAMApiService _iamApiSrv; + @Inject + public EntityManager _entityMgr; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @ACL + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IAMPolicyResponse.class, + required = true, description = "The ID of the iam policy") + private Long id; + + @Parameter(name = ApiConstants.IAM_ACTION, type = CommandType.STRING, required = true, description = "action api name.") + private String action; + + @Parameter(name = ApiConstants.ENTITY_TYPE, type = CommandType.STRING, required = false, description = "entity class simple name.") + private String entityType; + + @Parameter(name = ApiConstants.IAM_SCOPE, type = CommandType.STRING, + required = false, description = "iam permission scope") + private String scope; + + @Parameter(name = ApiConstants.IAM_SCOPE_ID, type = CommandType.STRING, required = false, description = "The UUID of the permission scope id") + private String scopeId; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getId() { + return id; + } + + + public String getAction() { + return action; + } + + public String getEntityType() { + return entityType; + } + + public String getScope() { + return scope; + } + + public Long getScopeId() { + // here we will convert the passed String UUID to Long ID since internally we store it as entity internal ID. + return _iamApiSrv.getPermissionScopeId(scope, entityType, scopeId); + } + + ///////////////////////////////////////////////////// + /////////////// 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("IAM policy Id: " + getId()); + // Only explicit ALLOW is supported for this release, no explicit deny + IAMPolicy result = _iamApiSrv.addIAMPermissionToIAMPolicy(id, entityType, PermissionScope.valueOf(scope), + getScopeId(), action, Permission.Allow, false); + if (result != null) { + IAMPolicyResponse response = _iamApiSrv.createIAMPolicyResponse(result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to grant permission to iam policy " + + getId()); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IAM_POLICY_GRANT; + } + + @Override + public String getEventDescription() { + return "granting permission to iam policy"; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.IAMPolicy; + } + +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/AttachIAMPolicyToAccountCmd.java b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/AttachIAMPolicyToAccountCmd.java new file mode 100644 index 00000000000..fc174cf1594 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/AttachIAMPolicyToAccountCmd.java @@ -0,0 +1,122 @@ +// 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.iam; + +import java.util.List; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.iam.IAMApiService; +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.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.iam.IAMPolicyResponse; +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 = "attachIAMPolicyToAccount", description = "attach iam policy to accounts", responseObject = SuccessResponse.class) +public class AttachIAMPolicyToAccountCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(AttachIAMPolicyToAccountCmd.class.getName()); + private static final String s_name = "attachiampolicytoaccountresponse"; + + @Inject + public IAMApiService _iamApiSrv; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @ACL + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IAMPolicyResponse.class, + required = true, description = "The ID of the iam policy") + private Long id; + + @ACL + @Parameter(name = ApiConstants.ACCOUNTS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AccountResponse.class, description = "comma separated list of account id that the policy will attach to.") + private List accountIdList; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getId() { + return id; + } + + + public List getAccountIdList() { + return accountIdList; + } + + ///////////////////////////////////////////////////// + /////////////// 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("IAM policy Id: " + getId()); + _iamApiSrv.attachIAMPolicyToAccounts(id, accountIdList); + SuccessResponse response = new SuccessResponse(); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IAM_ACCOUNT_POLICY_UPDATE; + } + + @Override + public String getEventDescription() { + return "adding IAM policy to accounts"; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.Account; + } + +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/AttachIAMPolicyToIAMGroupCmd.java b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/AttachIAMPolicyToIAMGroupCmd.java new file mode 100644 index 00000000000..1705c4a91c3 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/AttachIAMPolicyToIAMGroupCmd.java @@ -0,0 +1,127 @@ +// 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.iam; + +import java.util.List; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.iam.IAMApiService; +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.iam.IAMGroupResponse; +import org.apache.cloudstack.api.response.iam.IAMPolicyResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.iam.api.IAMGroup; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; + + +@APICommand(name = "attachIAMPolicyToIAMGroup", description = "attach iam policy to an iam group", responseObject = IAMGroupResponse.class) +public class AttachIAMPolicyToIAMGroupCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(AttachIAMPolicyToIAMGroupCmd.class.getName()); + private static final String s_name = "attachiampolicytoiamgroupresponse"; + + @Inject + public IAMApiService _iamApiSrv; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @ACL + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IAMGroupResponse.class, + required = true, description = "The ID of the iam group") + private Long id; + + @ACL + @Parameter(name = ApiConstants.IAM_POLICIES, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = IAMPolicyResponse.class, description = "comma separated list of iam policy id that are going to be applied to the iam group.") + private List policyIdList; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getId() { + return id; + } + + + public List getPolicyIdList() { + return policyIdList; + } + + ///////////////////////////////////////////////////// + /////////////// 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("IAM group Id: " + getId()); + IAMGroup result = _iamApiSrv.attachIAMPoliciesToGroup(policyIdList, id); + if (result != null){ + IAMGroupResponse response = _iamApiSrv.createIAMGroupResponse(result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add roles to iam group"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IAM_GROUP_UPDATE; + } + + @Override + public String getEventDescription() { + return "adding iam roles to iam group"; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.IAMGroup; + } + +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/CreateIAMGroupCmd.java b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/CreateIAMGroupCmd.java new file mode 100644 index 00000000000..d0b9bc6b1c1 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/CreateIAMGroupCmd.java @@ -0,0 +1,168 @@ +// 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.iam; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.iam.IAMApiService; +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.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.iam.IAMGroupResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.iam.api.IAMGroup; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.user.Account; + +@APICommand(name = "createIAMGroup", responseObject = IAMGroupResponse.class, description = "Creates an IAM group") +public class CreateIAMGroupCmd extends BaseAsyncCreateCmd { + public static final Logger s_logger = Logger.getLogger(CreateIAMGroupCmd.class.getName()); + + private static final String s_name = "createiamgroupresponse"; + + @Inject + public IAMApiService _iamApiSrv; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an account for the iam group. Must be used with domainId.") + private String accountName; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, description = "domainId of the account owning the iam group", entityType = DomainResponse.class) + private Long domainId; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "optional description of the iam group") + private String description; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the iam group") + private String name; + + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public String getAccountName() { + return accountName; + } + + public String getDescription() { + return description; + } + + public Long getDomainId() { + return domainId; + } + + public String getName() { + return name; + } + + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Account account = CallContext.current().getCallingAccount(); + if ((account == null) || _accountService.isAdmin(account.getType())) { + if ((domainId != null) && (accountName != null)) { + Account userAccount = _responseGenerator.findAccountByNameDomain(accountName, domainId); + if (userAccount != null) { + return userAccount.getId(); + } + } + } + + if (account != null) { + return account.getId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this + // command to SYSTEM so ERROR events + // are tracked + } + + @Override + public void execute() { + IAMGroup grp = _entityMgr.findById(IAMGroup.class, getEntityId()); + if (grp != null) { + IAMGroupResponse response = _iamApiSrv.createIAMGroupResponse(grp); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create iam group:" + name); + } + } + + @Override + public void create() throws ResourceAllocationException { + Account account = CallContext.current().getCallingAccount(); + IAMGroup result = _iamApiSrv.createIAMGroup(account, name, description); + if (result != null) { + setEntityId(result.getId()); + setEntityUuid(result.getUuid()); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create iam group entity" + name); + } + + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IAM_GROUP_CREATE; + } + + @Override + public String getEventDescription() { + return "creating IAM group"; + } + + @Override + public String getCreateEventType() { + return EventTypes.EVENT_IAM_GROUP_CREATE; + } + + @Override + public String getCreateEventDescription() { + return "creating IAM group"; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.IAMGroup; + } + +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/CreateIAMPolicyCmd.java b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/CreateIAMPolicyCmd.java new file mode 100644 index 00000000000..be863de308b --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/CreateIAMPolicyCmd.java @@ -0,0 +1,175 @@ +// 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.iam; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.iam.IAMApiService; +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.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.iam.IAMPolicyResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.iam.api.IAMPolicy; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.user.Account; + +@APICommand(name = "createIAMPolicy", responseObject = IAMPolicyResponse.class, description = "Creates an iam policy") +public class CreateIAMPolicyCmd extends BaseAsyncCreateCmd { + public static final Logger s_logger = Logger.getLogger(CreateIAMPolicyCmd.class.getName()); + + private static final String s_name = "createiampolicyresponse"; + + @Inject + public IAMApiService _iamApiSrv; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an account for the iam policy. Must be used with domainId.") + private String accountName; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, description = "domainId of the account owning the iam policy", entityType = DomainResponse.class) + private Long domainId; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "optional description of the iam policy") + private String description; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the iam policy") + private String name; + + @ACL + @Parameter(name = ApiConstants.IAM_PARENT_POLICY_ID, type = CommandType.UUID, description = "The ID of parent iam policy.", entityType = IAMPolicyResponse.class) + private Long parentPolicyId; + + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public String getAccountName() { + return accountName; + } + + public String getDescription() { + return description; + } + + public Long getDomainId() { + return domainId; + } + + public String getName() { + return name; + } + + public Long getParentPolicyId() { + return parentPolicyId; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Account account = CallContext.current().getCallingAccount(); + if ((account == null) || _accountService.isAdmin(account.getType())) { + if ((domainId != null) && (accountName != null)) { + Account userAccount = _responseGenerator.findAccountByNameDomain(accountName, domainId); + if (userAccount != null) { + return userAccount.getId(); + } + } + } + + if (account != null) { + return account.getId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this + // command to SYSTEM so ERROR events + // are tracked + } + + @Override + public void execute() { + IAMPolicy policy = _entityMgr.findById(IAMPolicy.class, getEntityId()); + if (policy != null) { + IAMPolicyResponse response = _iamApiSrv.createIAMPolicyResponse(policy); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create iam policy:" + name); + } + } + + @Override + public void create() throws ResourceAllocationException { + Account account = CallContext.current().getCallingAccount(); + IAMPolicy result = _iamApiSrv.createIAMPolicy(account, name, description, parentPolicyId); + if (result != null) { + setEntityId(result.getId()); + setEntityUuid(result.getUuid()); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create iam policy entity" + name); + } + + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IAM_POLICY_CREATE; + } + + @Override + public String getEventDescription() { + return "creating IAM policy"; + } + + @Override + public String getCreateEventType() { + return EventTypes.EVENT_IAM_POLICY_CREATE; + } + + @Override + public String getCreateEventDescription() { + return "creating IAM policy"; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.IAMPolicy; + } + +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/DeleteIAMGroupCmd.java b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/DeleteIAMGroupCmd.java new file mode 100644 index 00000000000..60b1e24fecb --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/DeleteIAMGroupCmd.java @@ -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.iam; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +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.SuccessResponse; +import org.apache.cloudstack.api.response.iam.IAMGroupResponse; +import org.apache.cloudstack.iam.IAMApiService; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "deleteIAMGroup", description = "Deletes acl group", responseObject = SuccessResponse.class) +public class DeleteIAMGroupCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DeleteIAMGroupCmd.class.getName()); + private static final String s_name = "deleteaclgroupresponse"; + + @Inject + public IAMApiService _aclApiSrv; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @ACL + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, description = "The ID of the acl group.", required = true, entityType = IAMGroupResponse.class) + private Long id; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + boolean result = _aclApiSrv.deleteIAMGroup(id); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete acl group"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IAM_GROUP_DELETE; + } + + @Override + public String getEventDescription() { + return "Deleting Acl group"; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.IAMGroup; + } +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/DeleteIAMPolicyCmd.java b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/DeleteIAMPolicyCmd.java new file mode 100644 index 00000000000..037f4cd5991 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/DeleteIAMPolicyCmd.java @@ -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.iam; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.iam.IAMApiService; +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.SuccessResponse; +import org.apache.cloudstack.api.response.iam.IAMPolicyResponse; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "deleteIAMPolicy", description = "Deletes iam policy", responseObject = SuccessResponse.class) +public class DeleteIAMPolicyCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DeleteIAMPolicyCmd.class.getName()); + private static final String s_name = "deleteiampolicyresponse"; + + @Inject + public IAMApiService _iamApiSrv; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @ACL + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, description = "The ID of the iam policy.", required = true, entityType = IAMPolicyResponse.class) + private Long id; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + boolean result = _iamApiSrv.deleteIAMPolicy(id); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete iam policy"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IAM_POLICY_DELETE; + } + + @Override + public String getEventDescription() { + return "Deleting IAM policy"; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.IAMPolicy; + } +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/ListIAMGroupsCmd.java b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/ListIAMGroupsCmd.java new file mode 100644 index 00000000000..ece87fa3379 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/ListIAMGroupsCmd.java @@ -0,0 +1,88 @@ +// 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.iam; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.iam.IAMApiService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListDomainResourcesCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.iam.IAMGroupResponse; + + +@APICommand(name = "listIAMGroups", description = "Lists iam groups", responseObject = IAMGroupResponse.class) +public class ListIAMGroupsCmd extends BaseListDomainResourcesCmd { + public static final Logger s_logger = Logger.getLogger(ListIAMGroupsCmd.class.getName()); + + private static final String s_name = "listiamgroupsresponse"; + + @Inject + public IAMApiService _iamApiSrv; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "lists iam groups by name") + private String iamGroupName; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, description = "list the iam group by the id provided", entityType = IAMGroupResponse.class) + private Long id; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + public String getIAMGroupName() { + return iamGroupName; + } + + + public Long getId(){ + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + + ListResponse response = _iamApiSrv.listIAMGroups(id, iamGroupName, getDomainId(), + getStartIndex(), getPageSizeVal()); + response.setResponseName(getCommandName()); + setResponseObject(response); + + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.IAMGroup; + } +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/ListIAMPoliciesCmd.java b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/ListIAMPoliciesCmd.java new file mode 100644 index 00000000000..096cc3b4ce8 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/ListIAMPoliciesCmd.java @@ -0,0 +1,88 @@ +// 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.iam; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.iam.IAMApiService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListDomainResourcesCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.iam.IAMPolicyResponse; + + +@APICommand(name = "listIAMPolicies", description = "Lists IAM policies", responseObject = IAMPolicyResponse.class) +public class ListIAMPoliciesCmd extends BaseListDomainResourcesCmd { + public static final Logger s_logger = Logger.getLogger(ListIAMPoliciesCmd.class.getName()); + + private static final String s_name = "listiampoliciesresponse"; + + @Inject + public IAMApiService _iamApiSrv; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "lists iam policies by name") + private String iamPolicyName; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, description = "list the iam policy by the id provided", entityType = IAMPolicyResponse.class) + private Long id; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + public String getIAMPolicyName() { + return iamPolicyName; + } + + + public Long getId(){ + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + + ListResponse response = _iamApiSrv.listIAMPolicies(id, iamPolicyName, getDomainId(), + getStartIndex(), getPageSizeVal()); + response.setResponseName(getCommandName()); + setResponseObject(response); + + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.IAMPolicy; + } +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/RemoveAccountFromIAMGroupCmd.java b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/RemoveAccountFromIAMGroupCmd.java new file mode 100644 index 00000000000..5ff5039c7b0 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/RemoveAccountFromIAMGroupCmd.java @@ -0,0 +1,127 @@ +// 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.iam; + +import java.util.List; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.iam.IAMApiService; +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.AccountResponse; +import org.apache.cloudstack.api.response.iam.IAMGroupResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.iam.api.IAMGroup; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; + + +@APICommand(name = "removeAccountFromIAMGroup", description = "remove accounts from an iam group", responseObject = IAMGroupResponse.class) +public class RemoveAccountFromIAMGroupCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(RemoveAccountFromIAMGroupCmd.class.getName()); + private static final String s_name = "removeaccountfromiamgroupresponse"; + + @Inject + public IAMApiService _iamApiSrv; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @ACL + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IAMGroupResponse.class, + required = true, description = "The ID of the iam group") + private Long id; + + @ACL + @Parameter(name = ApiConstants.ACCOUNTS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AccountResponse.class, description = "comma separated list of account id that are going to be assigned to the iam group.") + private List accountIdList; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getId() { + return id; + } + + + public List getAccountIdList() { + return accountIdList; + } + + ///////////////////////////////////////////////////// + /////////////// 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("IAM group Id: " + getId()); + IAMGroup result = _iamApiSrv.removeAccountsFromGroup(accountIdList, id); + if (result != null){ + IAMGroupResponse response = _iamApiSrv.createIAMGroupResponse(result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove accounts from iam group"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IAM_GROUP_UPDATE; + } + + @Override + public String getEventDescription() { + return "removing accounts from iam group"; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.IAMGroup; + } + +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/RemoveIAMPermissionFromIAMPolicyCmd.java b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/RemoveIAMPermissionFromIAMPolicyCmd.java new file mode 100644 index 00000000000..bf065a01a50 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/RemoveIAMPermissionFromIAMPolicyCmd.java @@ -0,0 +1,148 @@ +// 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.iam; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.acl.PermissionScope; +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.iam.IAMPolicyResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.iam.IAMApiService; +import org.apache.cloudstack.iam.api.IAMPolicy; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; + + +@APICommand(name = "removeIAMPermissionFromIAMPolicy", description = "Remove iam permission from an iam policy", responseObject = IAMPolicyResponse.class) +public class RemoveIAMPermissionFromIAMPolicyCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(RemoveIAMPermissionFromIAMPolicyCmd.class.getName()); + private static final String s_name = "removeiampermissionfromiampolicyresponse"; + + @Inject + public IAMApiService _iamApiSrv; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @ACL + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IAMPolicyResponse.class, + required = true, description = "The ID of the iam policy") + private Long id; + + @Parameter(name = ApiConstants.IAM_ACTION, type = CommandType.STRING, required = true, description = "action api name.") + private String action; + + @Parameter(name = ApiConstants.ENTITY_TYPE, type = CommandType.STRING, required = false, description = "entity class simple name.") + private String entityType; + + @Parameter(name = ApiConstants.IAM_SCOPE, type = CommandType.STRING, + required = false, description = "iam permission scope") + private String scope; + + @Parameter(name = ApiConstants.IAM_SCOPE_ID, type = CommandType.STRING, required = false, description = "The ID of the permission scope id") + private String scopeId; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getId() { + return id; + } + + + public String getAction() { + return action; + } + + public String getEntityType() { + return entityType; + } + + public String getScope() { + return scope; + } + + public Long getScopeId() { + // here we will convert the passed String UUID to Long ID since internally we store it as entity internal ID. + return _iamApiSrv.getPermissionScopeId(scope, entityType, scopeId); + } + + + ///////////////////////////////////////////////////// + /////////////// 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("IAM policy Id: " + getId()); + IAMPolicy result = _iamApiSrv.removeIAMPermissionFromIAMPolicy(id, entityType, PermissionScope.valueOf(scope), getScopeId(), action); + if (result != null) { + IAMPolicyResponse response = _iamApiSrv.createIAMPolicyResponse(result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove permission from iam policy " + getId()); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IAM_POLICY_REVOKE; + } + + @Override + public String getEventDescription() { + return "removing permission from iam policy"; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.IAMPolicy; + } + +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/RemoveIAMPolicyFromAccountCmd.java b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/RemoveIAMPolicyFromAccountCmd.java new file mode 100644 index 00000000000..48c2a7386b9 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/RemoveIAMPolicyFromAccountCmd.java @@ -0,0 +1,122 @@ +// 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.iam; + +import java.util.List; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +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.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.iam.IAMPolicyResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.iam.IAMApiService; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; + + +@APICommand(name = "removeIAMPolicyFromAccount", description = "remove iam policy from accounts", responseObject = SuccessResponse.class) +public class RemoveIAMPolicyFromAccountCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(RemoveIAMPolicyFromAccountCmd.class.getName()); + private static final String s_name = "removeiampolicyfromaccountresponse"; + + @Inject + public IAMApiService _iamApiSrv; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @ACL + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IAMPolicyResponse.class, + required = true, description = "The ID of the iam group") + private Long id; + + @ACL + @Parameter(name = ApiConstants.ACCOUNTS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AccountResponse.class, description = "comma separated list of iam policy id that are going to be applied to the iam group.") + private List accountIdList; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getId() { + return id; + } + + + public List getAccountIdList() { + return accountIdList; + } + + ///////////////////////////////////////////////////// + /////////////// 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("IAM policy Id: " + getId()); + _iamApiSrv.removeIAMPolicyFromAccounts(id, accountIdList); + SuccessResponse response = new SuccessResponse(); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IAM_ACCOUNT_POLICY_UPDATE; + } + + @Override + public String getEventDescription() { + return "removing iam policy from accounts"; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.Account; + } + +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/RemoveIAMPolicyFromIAMGroupCmd.java b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/RemoveIAMPolicyFromIAMGroupCmd.java new file mode 100644 index 00000000000..a99143d9f56 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/command/iam/RemoveIAMPolicyFromIAMGroupCmd.java @@ -0,0 +1,127 @@ +// 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.iam; + +import java.util.List; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.iam.IAMApiService; +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.iam.IAMGroupResponse; +import org.apache.cloudstack.api.response.iam.IAMPolicyResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.iam.api.IAMGroup; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; + + +@APICommand(name = "removeIAMPolicyFromIAMGroup", description = "remove iam policy from an iam group", responseObject = IAMGroupResponse.class) +public class RemoveIAMPolicyFromIAMGroupCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(RemoveIAMPolicyFromIAMGroupCmd.class.getName()); + private static final String s_name = "removeiampolicyfromiamgroupresponse"; + + @Inject + public IAMApiService _iamApiSrv; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @ACL + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IAMGroupResponse.class, + required = true, description = "The ID of the iam group") + private Long id; + + @ACL + @Parameter(name = ApiConstants.IAM_POLICIES, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = IAMPolicyResponse.class, description = "comma separated list of iam policy id that are going to be applied to the iam group.") + private List policyIdList; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getId() { + return id; + } + + + public List getRoleIdList() { + return policyIdList; + } + + ///////////////////////////////////////////////////// + /////////////// 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("IAM group Id: " + getId()); + IAMGroup result = _iamApiSrv.removeIAMPoliciesFromGroup(policyIdList, id); + if (result != null){ + IAMGroupResponse response = _iamApiSrv.createIAMGroupResponse(result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add roles to iam group"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IAM_GROUP_UPDATE; + } + + @Override + public String getEventDescription() { + return "removing IAM roles from IAM group"; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.IAMGroup; + } + +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/response/iam/IAMGroupResponse.java b/services/iam/plugin/src/org/apache/cloudstack/api/response/iam/IAMGroupResponse.java new file mode 100644 index 00000000000..af28d53ff3c --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/response/iam/IAMGroupResponse.java @@ -0,0 +1,193 @@ +// 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.iam; + +import java.util.LinkedHashSet; +import java.util.Set; + +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.api.response.ControlledViewEntityResponse; +import org.apache.cloudstack.iam.api.IAMGroup; + +import com.cloud.serializer.Param; + +@SuppressWarnings("unused") +@EntityReference(value = IAMGroup.class) +public class IAMGroupResponse extends BaseResponse implements ControlledViewEntityResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the iam group") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "the name of the iam group") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "the description of the iam group") + private String description; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the domain ID of the iam group") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain name of the iam role") + private String domainName; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account owning the policy") + private String accountName; + + @SerializedName(ApiConstants.IAM_MEMBER_ACCOUNTS) + @Param(description = "account names assigned to this iam group ") + private Set accountNameList; + + @SerializedName(ApiConstants.IAM_POLICIES) + @Param(description = "iam policies attached to this iam group ") + private Set policyNameList; + + public IAMGroupResponse() { + accountNameList = new LinkedHashSet(); + policyNameList = new LinkedHashSet(); + } + + @Override + public String getObjectId() { + return getId(); + } + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + + public void setName(String name) { + this.name = name; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + @Override + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + @Override + public void setAccountName(String accountName) { + this.accountName = accountName; + + } + + @Override + public void setProjectId(String projectId) { + // TODO Auto-generated method stub + + } + + @Override + public void setProjectName(String projectName) { + // TODO Auto-generated method stub + + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getDomainId() { + return domainId; + } + + public String getDomainName() { + return domainName; + } + + public String getAccountName() { + return accountName; + } + + public Set getAccountNameList() { + return accountNameList; + } + + public void setMemberAccounts(Set accts) { + accountNameList = accts; + } + + public void addMemberAccount(String acct) { + accountNameList.add(acct); + } + + public void setPolicyList(Set policies) { + policyNameList = policies; + } + + public void addPolicy(String policy) { + policyNameList.add(policy); + } + + public Set getPolicyList() { + return policyNameList; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.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; + IAMGroupResponse other = (IAMGroupResponse)obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } + +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/response/iam/IAMPermissionResponse.java b/services/iam/plugin/src/org/apache/cloudstack/api/response/iam/IAMPermissionResponse.java new file mode 100644 index 00000000000..b7af4dad4b2 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/response/iam/IAMPermissionResponse.java @@ -0,0 +1,125 @@ +// 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.iam; + +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.acl.IAMEntityType; +import org.apache.cloudstack.acl.PermissionScope; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.iam.api.IAMPolicyPermission; + +import com.cloud.serializer.Param; + +public class IAMPermissionResponse extends BaseResponse { + + @SerializedName(ApiConstants.IAM_ACTION) + @Param(description = "action of this permission") + private String action; + + @SerializedName(ApiConstants.ENTITY_TYPE) + @Param(description = "the entity type of this permission") + private IAMEntityType entityType; + + @SerializedName(ApiConstants.IAM_SCOPE) + @Param(description = "scope of this permission") + private PermissionScope scope; + + @SerializedName(ApiConstants.IAM_SCOPE_ID) + @Param(description = "scope id of this permission") + private Long scopeId; + + @SerializedName(ApiConstants.IAM_ALLOW_DENY) + @Param(description = "allow or deny of this permission") + private IAMPolicyPermission.Permission permission; + + public IAMEntityType getEntityType() { + return entityType; + } + + public void setEntityType(IAMEntityType entityType) { + this.entityType = entityType; + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public PermissionScope getScope() { + return scope; + } + + public void setScope(PermissionScope scope) { + this.scope = scope; + } + + public Long getScopeId() { + return scopeId; + } + + public void setScopeId(Long scopeId) { + this.scopeId = scopeId; + } + + public IAMPolicyPermission.Permission getPermission() { + return permission; + } + + public void setPermission(IAMPolicyPermission.Permission permission) { + this.permission = permission; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((action == null) ? 0 : action.hashCode()); + result = prime * result + ((entityType == null) ? 0 : entityType.hashCode()); + result = prime * result + ((scope == null) ? 0 : scope.hashCode()); + result = prime * result + ((scopeId == null) ? 0 : scopeId.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; + IAMPermissionResponse other = (IAMPermissionResponse) obj; + if ((entityType == null && other.entityType != null) || !entityType.equals(other.entityType)) { + return false; + } else if ((action == null && other.action != null) || !action.equals(other.action)) { + return false; + } else if ((scope == null && other.scope != null) || !scope.equals(other.scope)) { + return false; + } else if ((scopeId == null && other.scopeId != null) || !scopeId.equals(other.scopeId)) { + return false; + } + return true; + } + + + +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/api/response/iam/IAMPolicyResponse.java b/services/iam/plugin/src/org/apache/cloudstack/api/response/iam/IAMPolicyResponse.java new file mode 100644 index 00000000000..dc29369a39c --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/api/response/iam/IAMPolicyResponse.java @@ -0,0 +1,177 @@ +// 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.iam; + +import java.util.LinkedHashSet; +import java.util.Set; + +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.api.response.ControlledViewEntityResponse; +import org.apache.cloudstack.iam.api.IAMPolicy; + +import com.cloud.serializer.Param; + +@SuppressWarnings("unused") +@EntityReference(value = IAMPolicy.class) +public class IAMPolicyResponse extends BaseResponse implements ControlledViewEntityResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the iam policy") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "the name of the iam policy") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "the description of the iam policy") + private String description; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the domain ID of the iam policy") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain name of the iam policy") + private String domainName; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account owning the policy") + private String accountName; + + @SerializedName(ApiConstants.IAM_PERMISSIONS) + @Param(description = "set of permissions for the iam policy") + private Set permissionList; + + public IAMPolicyResponse() { + permissionList = new LinkedHashSet(); + } + + @Override + public String getObjectId() { + return getId(); + } + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + + public void setName(String name) { + this.name = name; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + @Override + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + public Set getPermissionList() { + return permissionList; + } + + public void setPermissionList(Set perms) { + permissionList = perms; + } + + public void addPermission(IAMPermissionResponse perm) { + permissionList.add(perm); + } + + @Override + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + @Override + public void setProjectId(String projectId) { + // TODO Auto-generated method stub + + } + + @Override + public void setProjectName(String projectName) { + // TODO Auto-generated method stub + + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getDomainId() { + return domainId; + } + + public String getDomainName() { + return domainName; + } + + public String getAccountName() { + return accountName; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.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; + IAMPolicyResponse other = (IAMPolicyResponse) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } + + + +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/iam/IAMApiService.java b/services/iam/plugin/src/org/apache/cloudstack/iam/IAMApiService.java new file mode 100644 index 00000000000..bb8f03b77fd --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/iam/IAMApiService.java @@ -0,0 +1,87 @@ +// 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.iam; + +import java.util.List; + +import org.apache.cloudstack.acl.PermissionScope; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.iam.IAMGroupResponse; +import org.apache.cloudstack.api.response.iam.IAMPolicyResponse; +import org.apache.cloudstack.iam.api.IAMGroup; +import org.apache.cloudstack.iam.api.IAMPolicy; +import org.apache.cloudstack.iam.api.IAMPolicyPermission; +import org.apache.cloudstack.iam.api.IAMPolicyPermission.Permission; + +import com.cloud.user.Account; +import com.cloud.utils.component.PluggableService; + +public interface IAMApiService extends PluggableService { + + /* ACL group related interfaces */ + IAMGroup createIAMGroup(Account caller, String iamGroupName, String description); + + boolean deleteIAMGroup(Long iamGroupId); + + List listIAMGroups(long accountId); + + IAMGroup addAccountsToGroup(List acctIds, Long groupId); + + IAMGroup removeAccountsFromGroup(List acctIds, Long groupId); + + /* IAM Policy related interfaces */ + IAMPolicy createIAMPolicy(Account caller, String iamPolicyName, String description, Long parentPolicyId); + + boolean deleteIAMPolicy(long iamPolicyId); + + List listIAMPolicies(long accountId); + + IAMGroup attachIAMPoliciesToGroup(List policyIds, Long groupId); + + IAMGroup removeIAMPoliciesFromGroup(List policyIds, Long groupId); + + void attachIAMPolicyToAccounts(Long policyId, List accountIds); + + void removeIAMPolicyFromAccounts(Long policyId, List accountIds); + + IAMPolicy addIAMPermissionToIAMPolicy(long iamPolicyId, String entityType, PermissionScope scope, Long scopeId, + String action, Permission perm, Boolean recursive); + + IAMPolicy removeIAMPermissionFromIAMPolicy(long iamPolicyId, String entityType, PermissionScope scope, Long scopeId, String action); + + IAMPolicyPermission getIAMPolicyPermission(long accountId, String entityType, String action); + + /* Utility routine to grant/revoke invidivual resource to list of accounts */ + void grantEntityPermissioinToAccounts(String entityType, Long entityId, AccessType accessType, String action, List accountIds); + + void revokeEntityPermissioinFromAccounts(String entityType, Long entityId, AccessType accessType, String action, List accountIds); + + /* Response Generation */ + IAMPolicyResponse createIAMPolicyResponse(IAMPolicy policy); + + IAMGroupResponse createIAMGroupResponse(IAMGroup group); + + ListResponse listIAMGroups(Long iamGroupId, String iamGroupName, + Long domainId, Long startIndex, Long pageSize); + + ListResponse listIAMPolicies(Long iamPolicyId, String iamPolicyName, + Long domainId, Long startIndex, Long pageSize); + + // Convert passed scope uuid to internal scope long id + Long getPermissionScopeId(String scope, String entityType, String scopeId); +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/iam/IAMApiServiceImpl.java b/services/iam/plugin/src/org/apache/cloudstack/iam/IAMApiServiceImpl.java new file mode 100644 index 00000000000..97519f26d56 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/iam/IAMApiServiceImpl.java @@ -0,0 +1,800 @@ +// 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.iam; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.acl.IAMEntityType; +import org.apache.cloudstack.acl.PermissionScope; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.affinity.AffinityGroupVO; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.api.command.iam.AddAccountToIAMGroupCmd; +import org.apache.cloudstack.api.command.iam.AddIAMPermissionToIAMPolicyCmd; +import org.apache.cloudstack.api.command.iam.AttachIAMPolicyToAccountCmd; +import org.apache.cloudstack.api.command.iam.AttachIAMPolicyToIAMGroupCmd; +import org.apache.cloudstack.api.command.iam.CreateIAMGroupCmd; +import org.apache.cloudstack.api.command.iam.CreateIAMPolicyCmd; +import org.apache.cloudstack.api.command.iam.DeleteIAMGroupCmd; +import org.apache.cloudstack.api.command.iam.DeleteIAMPolicyCmd; +import org.apache.cloudstack.api.command.iam.ListIAMGroupsCmd; +import org.apache.cloudstack.api.command.iam.ListIAMPoliciesCmd; +import org.apache.cloudstack.api.command.iam.RemoveAccountFromIAMGroupCmd; +import org.apache.cloudstack.api.command.iam.RemoveIAMPermissionFromIAMPolicyCmd; +import org.apache.cloudstack.api.command.iam.RemoveIAMPolicyFromAccountCmd; +import org.apache.cloudstack.api.command.iam.RemoveIAMPolicyFromIAMGroupCmd; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.iam.IAMGroupResponse; +import org.apache.cloudstack.api.response.iam.IAMPermissionResponse; +import org.apache.cloudstack.api.response.iam.IAMPolicyResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; +import org.apache.cloudstack.framework.messagebus.MessageBus; +import org.apache.cloudstack.framework.messagebus.MessageSubscriber; +import org.apache.cloudstack.iam.api.IAMGroup; +import org.apache.cloudstack.iam.api.IAMPolicy; +import org.apache.cloudstack.iam.api.IAMPolicyPermission; +import org.apache.cloudstack.iam.api.IAMPolicyPermission.Permission; +import org.apache.cloudstack.iam.api.IAMService; +import org.apache.cloudstack.iam.server.IAMGroupVO; +import org.apache.cloudstack.iam.server.IAMPolicyVO; +import org.apache.cloudstack.region.gslb.GlobalLoadBalancerRuleVO; + +import com.cloud.api.ApiServerService; +import com.cloud.domain.Domain; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; +import com.cloud.event.EventVO; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.UserIpv6AddressVO; +import com.cloud.network.VpnUserVO; +import com.cloud.network.as.AutoScalePolicyVO; +import com.cloud.network.as.AutoScaleVmGroupVO; +import com.cloud.network.as.AutoScaleVmProfileVO; +import com.cloud.network.as.ConditionVO; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.MonitoringServiceVO; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.RemoteAccessVpnVO; +import com.cloud.network.dao.Site2SiteCustomerGatewayVO; +import com.cloud.network.dao.Site2SiteVpnConnectionVO; +import com.cloud.network.dao.Site2SiteVpnGatewayVO; +import com.cloud.network.dao.SslCertVO; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.rules.PortForwardingRuleVO; +import com.cloud.network.security.SecurityGroupVO; +import com.cloud.network.vpc.StaticRouteVO; +import com.cloud.network.vpc.VpcGatewayVO; +import com.cloud.network.vpc.VpcVO; +import com.cloud.projects.ProjectInvitationVO; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VolumeVO; +import com.cloud.tags.ResourceTagVO; +import com.cloud.template.TemplateManager; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.DomainManager; +import com.cloud.user.SSHKeyPairVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.Pair; +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.vm.InstanceGroupVO; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.dao.NicIpAliasVO; +import com.cloud.vm.dao.NicSecondaryIpVO; +import com.cloud.vm.snapshot.VMSnapshotVO; + +@Local(value = {IAMApiService.class}) +public class IAMApiServiceImpl extends ManagerBase implements IAMApiService, Manager { + + public static final Logger s_logger = Logger.getLogger(IAMApiServiceImpl.class); + private String _name; + + @Inject + ApiServerService _apiServer; + + @Inject + IAMService _iamSrv; + + @Inject + DomainDao _domainDao; + + @Inject + AccountDao _accountDao; + + @Inject + AccountManager _accountMgr; + + @Inject + MessageBus _messageBus; + + @Inject + EntityManager _entityMgr; + + private static final Map> s_typeMap = new HashMap>(); + static { + s_typeMap.put(IAMEntityType.VirtualMachine, VMInstanceVO.class); + s_typeMap.put(IAMEntityType.Volume, VolumeVO.class); + s_typeMap.put(IAMEntityType.ResourceTag, ResourceTagVO.class); + s_typeMap.put(IAMEntityType.Account, AccountVO.class); + s_typeMap.put(IAMEntityType.AffinityGroup, AffinityGroupVO.class); + s_typeMap.put(IAMEntityType.AutoScalePolicy, AutoScalePolicyVO.class); + s_typeMap.put(IAMEntityType.AutoScaleVmProfile, AutoScaleVmProfileVO.class); + s_typeMap.put(IAMEntityType.AutoScaleVmGroup, AutoScaleVmGroupVO.class); + s_typeMap.put(IAMEntityType.Condition, ConditionVO.class); + s_typeMap.put(IAMEntityType.Vpc, VpcVO.class); + s_typeMap.put(IAMEntityType.VpcGateway, VpcGatewayVO.class); + s_typeMap.put(IAMEntityType.PrivateGateway, RemoteAccessVpnVO.class); + s_typeMap.put(IAMEntityType.VpnUser, VpnUserVO.class); + s_typeMap.put(IAMEntityType.VMSnapshot, VMSnapshotVO.class); + s_typeMap.put(IAMEntityType.VirtualMachineTemplate, VMTemplateVO.class); + s_typeMap.put(IAMEntityType.UserIpv6Address, UserIpv6AddressVO.class); + s_typeMap.put(IAMEntityType.StaticRoute, StaticRouteVO.class); + s_typeMap.put(IAMEntityType.SSHKeyPair, SSHKeyPairVO.class); + s_typeMap.put(IAMEntityType.Snapshot, SnapshotVO.class); + s_typeMap.put(IAMEntityType.Site2SiteVpnGateway, Site2SiteVpnGatewayVO.class); + s_typeMap.put(IAMEntityType.Site2SiteCustomerGateway, Site2SiteCustomerGatewayVO.class); + s_typeMap.put(IAMEntityType.Site2SiteVpnConnection, Site2SiteVpnConnectionVO.class); + s_typeMap.put(IAMEntityType.SecurityGroup, SecurityGroupVO.class); + s_typeMap.put(IAMEntityType.RemoteAccessVpn, RemoteAccessVpnVO.class); + s_typeMap.put(IAMEntityType.PublicIpAddress, IPAddressVO.class); + s_typeMap.put(IAMEntityType.ProjectInvitation, ProjectInvitationVO.class); + s_typeMap.put(IAMEntityType.NicSecondaryIp, NicSecondaryIpVO.class); + s_typeMap.put(IAMEntityType.NicIpAlias, NicIpAliasVO.class); + s_typeMap.put(IAMEntityType.Network, NetworkVO.class); + s_typeMap.put(IAMEntityType.IpAddress, IPAddressVO.class); + s_typeMap.put(IAMEntityType.InstanceGroup, InstanceGroupVO.class); + s_typeMap.put(IAMEntityType.GlobalLoadBalancerRule, GlobalLoadBalancerRuleVO.class); + s_typeMap.put(IAMEntityType.FirewallRule, FirewallRuleVO.class); + s_typeMap.put(IAMEntityType.PortForwardingRule, PortForwardingRuleVO.class); + s_typeMap.put(IAMEntityType.Event, EventVO.class); + s_typeMap.put(IAMEntityType.AsyncJob, AsyncJobVO.class); + s_typeMap.put(IAMEntityType.IAMGroup, IAMGroupVO.class); + s_typeMap.put(IAMEntityType.IAMPolicy, IAMPolicyVO.class); + s_typeMap.put(IAMEntityType.MonitorService, MonitoringServiceVO.class); + s_typeMap.put(IAMEntityType.SSLCert, SslCertVO.class); + } + + @Override + public boolean configure(final String name, final Map params) throws ConfigurationException { + _messageBus.subscribe(AccountManager.MESSAGE_ADD_ACCOUNT_EVENT, new MessageSubscriber() { + @Override + public void onPublishMessage(String senderAddress, String subject, Object obj) { + HashMap acctGroupMap = (HashMap) obj; + for (Long accountId : acctGroupMap.keySet()) { + Long groupId = acctGroupMap.get(accountId); + s_logger.debug("MessageBus message: new Account Added: " + accountId + ", adding it to groupId :" + + groupId); + addAccountToIAMGroup(accountId, groupId); + // add it to domain group too + AccountVO account = _accountDao.findById(accountId); + Domain domain = _domainDao.findById(account.getDomainId()); + if (domain != null) { + List domainGroups = listDomainGroup(domain); + + if (domainGroups != null) { + for (IAMGroup group : domainGroups) { + addAccountToIAMGroup(accountId, new Long(group.getId())); + } + } + } + } + } + }); + + _messageBus.subscribe(AccountManager.MESSAGE_REMOVE_ACCOUNT_EVENT, new MessageSubscriber() { + @Override + public void onPublishMessage(String senderAddress, String subject, Object obj) { + Long accountId = ((Long) obj); + if (accountId != null) { + s_logger.debug("MessageBus message: Account removed: " + accountId + + ", releasing the group associations"); + removeAccountFromIAMGroups(accountId); + } + } + }); + + _messageBus.subscribe(DomainManager.MESSAGE_ADD_DOMAIN_EVENT, new MessageSubscriber() { + @Override + public void onPublishMessage(String senderAddress, String subject, Object obj) { + Long domainId = ((Long) obj); + if (domainId != null) { + s_logger.debug("MessageBus message: new Domain created: " + domainId + ", creating a new group"); + Domain domain = _domainDao.findById(domainId); + _iamSrv.createIAMGroup("DomainGrp-" + domain.getUuid(), "Domain group", domain.getPath()); + } + } + }); + + _messageBus.subscribe(DomainManager.MESSAGE_REMOVE_DOMAIN_EVENT, new MessageSubscriber() { + @Override + public void onPublishMessage(String senderAddress, String subject, Object obj) { + Long domainId = ((Long) obj); + if (domainId != null) { + s_logger.debug("MessageBus message: Domain removed: " + domainId + ", removing the domain group"); + Domain domain = _domainDao.findById(domainId); + List groups = listDomainGroup(domain); + for (IAMGroup group : groups) { + _iamSrv.deleteIAMGroup(group.getId()); + } + } + } + }); + + _messageBus.subscribe(TemplateManager.MESSAGE_REGISTER_PUBLIC_TEMPLATE_EVENT, new MessageSubscriber() { + @Override + public void onPublishMessage(String senderAddress, String subject, Object obj) { + Long templateId = (Long)obj; + if (templateId != null) { + s_logger.debug("MessageBus message: new public template registered: " + templateId + ", grant permission to domain admin and normal user policies"); + _iamSrv.addIAMPermissionToIAMPolicy(new Long(Account.ACCOUNT_TYPE_DOMAIN_ADMIN + 1), IAMEntityType.VirtualMachineTemplate.toString(), + PermissionScope.RESOURCE.toString(), templateId, "listTemplates", AccessType.UseEntry.toString(), Permission.Allow, false); + _iamSrv.addIAMPermissionToIAMPolicy(new Long(Account.ACCOUNT_TYPE_NORMAL + 1), IAMEntityType.VirtualMachineTemplate.toString(), + PermissionScope.RESOURCE.toString(), templateId, "listTemplates", AccessType.UseEntry.toString(), Permission.Allow, false); + } + } + }); + + _messageBus.subscribe(TemplateManager.MESSAGE_RESET_TEMPLATE_PERMISSION_EVENT, new MessageSubscriber() { + @Override + public void onPublishMessage(String senderAddress, String subject, Object obj) { + Long templateId = (Long)obj; + if (templateId != null) { + s_logger.debug("MessageBus message: reset template permission: " + templateId); + resetTemplatePermission(templateId); + } + } + }); + + _messageBus.subscribe(EntityManager.MESSAGE_REMOVE_ENTITY_EVENT, new MessageSubscriber() { + @Override + public void onPublishMessage(String senderAddress, String subject, Object obj) { + Pair entity = (Pair)obj; + if (entity != null) { + String entityType = entity.first().toString(); + Long entityId = entity.second(); + s_logger.debug("MessageBus message: delete an entity: (" + entityType + "," + entityId + "), remove its related permission"); + _iamSrv.removeIAMPermissionForEntity(entityType, entityId); + } + } + }); + + + _messageBus.subscribe(EntityManager.MESSAGE_GRANT_ENTITY_EVENT, new MessageSubscriber() { + @Override + public void onPublishMessage(String senderAddress, String subject, Object obj) { + Map permit = (Map)obj; + if (permit != null) { + String entityType = (String)permit.get(ApiConstants.ENTITY_TYPE); + Long entityId = (Long)permit.get(ApiConstants.ENTITY_ID); + AccessType accessType = (AccessType)permit.get(ApiConstants.ACCESS_TYPE); + String action = (String)permit.get(ApiConstants.IAM_ACTION); + List acctIds = (List)permit.get(ApiConstants.ACCOUNTS); + s_logger.debug("MessageBus message: grant accounts permission to an entity: (" + entityType + "," + entityId + ")"); + grantEntityPermissioinToAccounts(entityType, entityId, accessType, action, acctIds); + } + } + }); + + _messageBus.subscribe(EntityManager.MESSAGE_REVOKE_ENTITY_EVENT, new MessageSubscriber() { + @Override + public void onPublishMessage(String senderAddress, String subject, Object obj) { + Map permit = (Map)obj; + if (permit != null) { + String entityType = (String)permit.get(ApiConstants.ENTITY_TYPE); + Long entityId = (Long)permit.get(ApiConstants.ENTITY_ID); + AccessType accessType = (AccessType)permit.get(ApiConstants.ACCESS_TYPE); + String action = (String)permit.get(ApiConstants.IAM_ACTION); + List acctIds = (List)permit.get(ApiConstants.ACCOUNTS); + s_logger.debug("MessageBus message: revoke from accounts permission to an entity: (" + entityType + "," + entityId + ")"); + revokeEntityPermissioinFromAccounts(entityType, entityId, accessType, action, acctIds); + } + } + }); + + _messageBus.subscribe(EntityManager.MESSAGE_ADD_DOMAIN_WIDE_ENTITY_EVENT, new MessageSubscriber() { + @Override + public void onPublishMessage(String senderAddress, String subject, Object obj) { + Map params = (Map) obj; + if (params != null) { + addDomainWideResourceAccess(params); + } + } + }); + + return super.configure(name, params); + } + + private void addDomainWideResourceAccess(Map params) { + + IAMEntityType entityType = (IAMEntityType)params.get(ApiConstants.ENTITY_TYPE); + Long entityId = (Long) params.get(ApiConstants.ENTITY_ID); + Long domainId = (Long) params.get(ApiConstants.DOMAIN_ID); + Boolean isRecursive = (Boolean) params.get(ApiConstants.SUBDOMAIN_ACCESS); + + if (entityType == IAMEntityType.Network) { + createPolicyAndAddToDomainGroup("DomainWideNetwork-" + entityId, "domain wide network", entityType.toString(), + entityId, "listNetworks", AccessType.UseEntry, domainId, isRecursive); + } else if (entityType == IAMEntityType.AffinityGroup) { + createPolicyAndAddToDomainGroup("DomainWideNetwork-" + entityId, "domain wide affinityGroup", entityType.toString(), + entityId, "listAffinityGroups", AccessType.UseEntry, domainId, isRecursive); + } + + } + + private void createPolicyAndAddToDomainGroup(String policyName, String description, String entityType, + Long entityId, String action, AccessType accessType, Long domainId, Boolean recursive) { + + Domain domain = _domainDao.findById(domainId); + if (domain != null) { + IAMPolicy policy = _iamSrv.createIAMPolicy(policyName, description, null, domain.getPath()); + _iamSrv.addIAMPermissionToIAMPolicy(policy.getId(), entityType, PermissionScope.RESOURCE.toString(), + entityId, action, accessType.toString(), Permission.Allow, recursive); + List policyList = new ArrayList(); + policyList.add(new Long(policy.getId())); + + List domainGroups = listDomainGroup(domain); + if (domainGroups != null) { + for (IAMGroup group : domainGroups) { + _iamSrv.attachIAMPoliciesToGroup(policyList, group.getId()); + } + } + } + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_IAM_GROUP_CREATE, eventDescription = "Creating Acl Group", create = true) + public IAMGroup createIAMGroup(Account caller, String iamGroupName, String description) { + Long domainId = caller.getDomainId(); + Domain callerDomain = _domainDao.findById(domainId); + if (callerDomain == null) { + throw new InvalidParameterValueException("Caller does not have a domain"); + } + return _iamSrv.createIAMGroup(iamGroupName, description, callerDomain.getPath()); + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_IAM_GROUP_DELETE, eventDescription = "Deleting Acl Group") + public boolean deleteIAMGroup(final Long iamGroupId) { + return _iamSrv.deleteIAMGroup(iamGroupId); + } + + @Override + public List listIAMGroups(long accountId) { + return _iamSrv.listIAMGroups(accountId); + } + + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_IAM_GROUP_UPDATE, eventDescription = "Adding accounts to acl group") + public IAMGroup addAccountsToGroup(final List acctIds, final Long groupId) { + return _iamSrv.addAccountsToGroup(acctIds, groupId); + } + + + private void removeAccountFromIAMGroups(long accountId) { + List groups = listIAMGroups(accountId); + List accts = new ArrayList(); + accts.add(accountId); + if (groups != null) { + for (IAMGroup grp : groups) { + removeAccountsFromGroup(accts, grp.getId()); + } + } + } + + private void addAccountToIAMGroup(long accountId, long groupId) { + List accts = new ArrayList(); + accts.add(accountId); + addAccountsToGroup(accts, groupId); + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_IAM_GROUP_UPDATE, eventDescription = "Removing accounts from acl group") + public IAMGroup removeAccountsFromGroup(final List acctIds, final Long groupId) { + return _iamSrv.removeAccountsFromGroup(acctIds, groupId); + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_IAM_POLICY_CREATE, eventDescription = "Creating IAM Policy", create = true) + public IAMPolicy createIAMPolicy(Account caller, final String iamPolicyName, final String description, final Long parentPolicyId) { + Long domainId = caller.getDomainId(); + Domain callerDomain = _domainDao.findById(domainId); + if (callerDomain == null) { + throw new InvalidParameterValueException("Caller does not have a domain"); + } + return _iamSrv.createIAMPolicy(iamPolicyName, description, parentPolicyId, callerDomain.getPath()); + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_IAM_POLICY_DELETE, eventDescription = "Deleting IAM Policy") + public boolean deleteIAMPolicy(final long iamPolicyId) { + return _iamSrv.deleteIAMPolicy(iamPolicyId); + } + + + @Override + public List listIAMPolicies(long accountId) { + return _iamSrv.listIAMPolicies(accountId); + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_IAM_GROUP_UPDATE, eventDescription = "Attaching policy to acl group") + public IAMGroup attachIAMPoliciesToGroup(final List policyIds, final Long groupId) { + return _iamSrv.attachIAMPoliciesToGroup(policyIds, groupId); + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_IAM_GROUP_UPDATE, eventDescription = "Removing policies from acl group") + public IAMGroup removeIAMPoliciesFromGroup(final List policyIds, final Long groupId) { + return _iamSrv.removeIAMPoliciesFromGroup(policyIds, groupId); + } + + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_IAM_ACCOUNT_POLICY_UPDATE, eventDescription = "Attaching policy to accounts") + public void attachIAMPolicyToAccounts(final Long policyId, final List accountIds) { + _iamSrv.attachIAMPolicyToAccounts(policyId, accountIds); + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_IAM_ACCOUNT_POLICY_UPDATE, eventDescription = "Removing policy from accounts") + public void removeIAMPolicyFromAccounts(final Long policyId, final List accountIds) { + _iamSrv.removeIAMPolicyFromAccounts(policyId, accountIds); + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_IAM_POLICY_GRANT, eventDescription = "Granting acl permission to IAM Policy") + public IAMPolicy addIAMPermissionToIAMPolicy(long iamPolicyId, String entityType, PermissionScope scope, + Long scopeId, String action, Permission perm, Boolean recursive) { + Class cmdClass = _apiServer.getCmdClass(action); + AccessType accessType = null; + if (BaseListCmd.class.isAssignableFrom(cmdClass)) { + accessType = AccessType.UseEntry; + } + return _iamSrv.addIAMPermissionToIAMPolicy(iamPolicyId, entityType, scope.toString(), scopeId, action, + accessType.toString(), perm, recursive); + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_IAM_POLICY_REVOKE, eventDescription = "Revoking acl permission from IAM Policy") + public IAMPolicy removeIAMPermissionFromIAMPolicy(long iamPolicyId, String entityType, PermissionScope scope, Long scopeId, String action) { + return _iamSrv.removeIAMPermissionFromIAMPolicy(iamPolicyId, entityType, scope.toString(), scopeId, action); + } + + @Override + public IAMPolicyPermission getIAMPolicyPermission(long accountId, String entityType, String action) { + List policies = _iamSrv.listIAMPolicies(accountId); + IAMPolicyPermission curPerm = null; + for (IAMPolicy policy : policies) { + List perms = _iamSrv.listPolicyPermissionByActionAndEntity(policy.getId(), action, + entityType); + if (perms == null || perms.size() == 0) + continue; + IAMPolicyPermission perm = perms.get(0); // just pick one + if (curPerm == null) { + curPerm = perm; + } else if (PermissionScope.valueOf(perm.getScope()).greaterThan(PermissionScope.valueOf(curPerm.getScope()))) { + // pick the more relaxed allowed permission + curPerm = perm; + } + } + + return curPerm; + } + + + @Override + public IAMPolicyResponse createIAMPolicyResponse(IAMPolicy policy) { + IAMPolicyResponse response = new IAMPolicyResponse(); + response.setId(policy.getUuid()); + response.setName(policy.getName()); + response.setDescription(policy.getDescription()); + String domainPath = policy.getPath(); + if (domainPath != null) { + DomainVO domain = _domainDao.findDomainByPath(domainPath); + if (domain != null) { + response.setDomainId(domain.getUuid()); + response.setDomainName(domain.getName()); + } + } + long accountId = policy.getAccountId(); + AccountVO owner = _accountDao.findById(accountId); + if (owner != null) { + response.setAccountName(owner.getAccountName()); + } + // find permissions associated with this policy + List permissions = _iamSrv.listPolicyPermissions(policy.getId()); + if (permissions != null && permissions.size() > 0) { + for (IAMPolicyPermission permission : permissions) { + IAMPermissionResponse perm = new IAMPermissionResponse(); + perm.setAction(permission.getAction()); + if (permission.getEntityType() != null) { + perm.setEntityType(IAMEntityType.valueOf(permission.getEntityType())); + } + if (permission.getScope() != null) { + perm.setScope(PermissionScope.valueOf(permission.getScope())); + } + perm.setScopeId(permission.getScopeId()); + perm.setPermission(permission.getPermission()); + response.addPermission(perm); + } + } + response.setObjectName("aclpolicy"); + return response; + } + + @Override + public IAMGroupResponse createIAMGroupResponse(IAMGroup group) { + IAMGroupResponse response = new IAMGroupResponse(); + response.setId(group.getUuid()); + response.setName(group.getName()); + response.setDescription(group.getDescription()); + String domainPath = group.getPath(); + if (domainPath != null) { + DomainVO domain = _domainDao.findDomainByPath(domainPath); + if (domain != null) { + response.setDomainId(domain.getUuid()); + response.setDomainName(domain.getName()); + } + } + long accountId = group.getAccountId(); + AccountVO owner = _accountDao.findById(accountId); + if (owner != null) { + response.setAccountName(owner.getAccountName()); + } + // find all the members in this group + List members = _iamSrv.listAccountsByGroup(group.getId()); + if (members != null && members.size() > 0) { + for (Long member : members) { + AccountVO mem = _accountDao.findById(member); + if (mem != null) { + response.addMemberAccount(mem.getAccountName()); + } + } + } + + // find all the policies attached to this group + List policies = _iamSrv.listIAMPoliciesByGroup(group.getId()); + if (policies != null && policies.size() > 0) { + for (IAMPolicy policy : policies) { + response.addPolicy(policy.getName()); + } + } + + response.setObjectName("aclgroup"); + return response; + + } + + public List listDomainGroup(Domain domain) { + + if (domain != null) { + String domainPath = domain.getPath(); + // search for groups + Pair, Integer> result = _iamSrv.listIAMGroups(null, "DomainGrp-" + domain.getUuid(), + domainPath, null, null); + return result.first(); + } + return new ArrayList(); + + } + + @Override + public ListResponse listIAMGroups(Long iamGroupId, String iamGroupName, Long domainId, Long startIndex, Long pageSize) { + // acl check + Account caller = CallContext.current().getCallingAccount(); + + Domain domain = null; + if (domainId != null) { + domain = _domainDao.findById(domainId); + if (domain == null) { + throw new InvalidParameterValueException("Domain id=" + domainId + " doesn't exist"); + } + + _accountMgr.checkAccess(caller, domain); + } else { + domain = _domainDao.findById(caller.getDomainId()); + } + String domainPath = domain.getPath(); + // search for groups + Pair, Integer> result = _iamSrv.listIAMGroups(iamGroupId, iamGroupName, domainPath, startIndex, pageSize); + // generate group response + ListResponse response = new ListResponse(); + List groupResponses = new ArrayList(); + for (IAMGroup group : result.first()) { + IAMGroupResponse resp = createIAMGroupResponse(group); + groupResponses.add(resp); + } + response.setResponses(groupResponses, result.second()); + return response; + } + + @Override + public ListResponse listIAMPolicies(Long iamPolicyId, String iamPolicyName, Long domainId, Long startIndex, + Long pageSize) { + // acl check + Account caller = CallContext.current().getCallingAccount(); + + Domain domain = null; + if (domainId != null) { + domain = _domainDao.findById(domainId); + if (domain == null) { + throw new InvalidParameterValueException("Domain id=" + domainId + " doesn't exist"); + } + + _accountMgr.checkAccess(caller, domain); + } else { + domain = _domainDao.findById(caller.getDomainId()); + } + String domainPath = domain.getPath(); + // search for policies + Pair, Integer> result = _iamSrv.listIAMPolicies(iamPolicyId, iamPolicyName, domainPath, startIndex, pageSize); + // generate policy response + ListResponse response = new ListResponse(); + List policyResponses = new ArrayList(); + for (IAMPolicy policy : result.first()) { + IAMPolicyResponse resp = createIAMPolicyResponse(policy); + policyResponses.add(resp); + } + response.setResponses(policyResponses, result.second()); + return response; + } + + @Override + public void grantEntityPermissioinToAccounts(String entityType, Long entityId, AccessType accessType, String action, List accountIds) { + // check if there is already a policy with only this permission added to it + IAMPolicy policy = _iamSrv.getResourceGrantPolicy(entityType, entityId, accessType.toString(), action); + if (policy == null) { + // not found, just create a policy with resource grant permission + Account caller = CallContext.current().getCallingAccount(); + String aclPolicyName = "policyGrant" + entityType + entityId; + String description = "Policy to grant permission to " + entityType + entityId; + policy = createIAMPolicy(caller, aclPolicyName, description, null); + // add permission to this policy + addIAMPermissionToIAMPolicy(policy.getId(), entityType, PermissionScope.RESOURCE, entityId, action, Permission.Allow, false); + } + // attach this policy to list of accounts if not attached already + Long policyId = policy.getId(); + for (Long acctId : accountIds) { + if (!isPolicyAttachedToAccount(policyId, acctId)) { + attachIAMPolicyToAccounts(policyId, Collections.singletonList(acctId)); + } + } + } + + @Override + public void revokeEntityPermissioinFromAccounts(String entityType, Long entityId, AccessType accessType, String action, List accountIds) { + // there should already a policy with only this permission added to it, this call is mainly used + IAMPolicy policy = _iamSrv.getResourceGrantPolicy(entityType, entityId, accessType.toString(), action); + if (policy == null) { + s_logger.warn("Cannot find a policy associated with this entity permissioin to be revoked, just return"); + return; + } + // detach this policy from list of accounts if not detached already + Long policyId = policy.getId(); + for (Long acctId : accountIds) { + if (isPolicyAttachedToAccount(policyId, acctId)) { + removeIAMPolicyFromAccounts(policyId, Collections.singletonList(acctId)); + } + } + + } + + private boolean isPolicyAttachedToAccount(Long policyId, Long accountId) { + List pList = listIAMPolicies(accountId); + for (IAMPolicy p : pList) { + if (p.getId() == policyId.longValue()) { + return true; + } + } + return false; + } + + private void resetTemplatePermission(Long templateId){ + // reset template will change template to private, so we need to remove its permission for domain admin and normal user group + _iamSrv.removeIAMPermissionFromIAMPolicy(new Long(Account.ACCOUNT_TYPE_DOMAIN_ADMIN + 1), IAMEntityType.VirtualMachineTemplate.toString(), + PermissionScope.RESOURCE.toString(), templateId, "listTemplates"); + _iamSrv.removeIAMPermissionFromIAMPolicy(new Long(Account.ACCOUNT_TYPE_NORMAL + 1), IAMEntityType.VirtualMachineTemplate.toString(), + PermissionScope.RESOURCE.toString(), templateId, "listTemplates"); + // check if there is a policy with only UseEntry permission for this template added + IAMPolicy policy = _iamSrv.getResourceGrantPolicy(IAMEntityType.VirtualMachineTemplate.toString(), templateId, AccessType.UseEntry.toString(), "listTemplates"); + if ( policy == null ){ + s_logger.info("No policy found for this template grant: " + templateId + ", no detach to be done"); + return; + } + // delete the policy, which should detach it from groups and accounts + _iamSrv.deleteIAMPolicy(policy.getId()); + + } + + @Override + public Long getPermissionScopeId(String scope, String entityType, String scopeId) { + if (scopeId.equals("-1")) { + return -1L; + } + PermissionScope permScope = PermissionScope.valueOf(scope); + InternalIdentity entity = null; + switch (permScope) { + case DOMAIN: + entity = _domainDao.findByUuid(scopeId); + break; + case ACCOUNT: + entity = _accountDao.findByUuid(scopeId); + break; + case RESOURCE: + Class clazz = s_typeMap.get(IAMEntityType.valueOf(entityType)); + entity = (InternalIdentity)_entityMgr.findByUuid(clazz, scopeId); + } + + if (entity != null) { + return entity.getId(); + } + throw new InvalidParameterValueException("Unable to find scopeId " + scopeId + " with scope " + scope + " and type " + entityType); + } + + @Override + public List> getCommands() { + List> cmdList = new ArrayList>(); + cmdList.add(CreateIAMPolicyCmd.class); + cmdList.add(DeleteIAMPolicyCmd.class); + cmdList.add(ListIAMPoliciesCmd.class); + cmdList.add(AddIAMPermissionToIAMPolicyCmd.class); + cmdList.add(RemoveIAMPermissionFromIAMPolicyCmd.class); + cmdList.add(AttachIAMPolicyToIAMGroupCmd.class); + cmdList.add(RemoveIAMPolicyFromIAMGroupCmd.class); + cmdList.add(CreateIAMGroupCmd.class); + cmdList.add(DeleteIAMGroupCmd.class); + cmdList.add(ListIAMGroupsCmd.class); + cmdList.add(AddAccountToIAMGroupCmd.class); + cmdList.add(RemoveAccountFromIAMGroupCmd.class); + cmdList.add(AttachIAMPolicyToAccountCmd.class); + cmdList.add(RemoveIAMPolicyFromAccountCmd.class); + return cmdList; + } +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/iam/RoleBasedAPIAccessChecker.java b/services/iam/plugin/src/org/apache/cloudstack/iam/RoleBasedAPIAccessChecker.java new file mode 100644 index 00000000000..fb75db310f4 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/iam/RoleBasedAPIAccessChecker.java @@ -0,0 +1,273 @@ +// 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.iam; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ejb.Local; +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.acl.APIChecker; +import org.apache.cloudstack.acl.IAMEntityType; +import org.apache.cloudstack.acl.PermissionScope; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.iam.api.IAMPolicy; +import org.apache.cloudstack.iam.api.IAMPolicyPermission; +import org.apache.cloudstack.iam.api.IAMPolicyPermission.Permission; +import org.apache.cloudstack.iam.api.IAMService; + +import com.cloud.api.ApiServerService; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.user.Account; +import com.cloud.user.AccountService; +import com.cloud.user.User; +import com.cloud.utils.PropertiesUtil; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.component.PluggableService; +import com.cloud.utils.exception.CloudRuntimeException; + +//This is the Role Based API access checker that grab's the account's roles +//based on the set of roles, access is granted if any of the role has access to the api +@Local(value=APIChecker.class) +public class RoleBasedAPIAccessChecker extends AdapterBase implements APIChecker { + + protected static final Logger s_logger = Logger.getLogger(RoleBasedAPIAccessChecker.class); + + @Inject + AccountService _accountService; + @Inject + ApiServerService _apiServer; + @Inject + IAMService _iamSrv; + @Inject + VMTemplateDao _templateDao; + + Set commandsPropertiesOverrides = new HashSet(); + Map> commandsPropertiesRoleBasedApisMap = new HashMap>(); + + List _services; + + protected RoleBasedAPIAccessChecker() { + super(); + for (RoleType roleType : RoleType.values()) { + commandsPropertiesRoleBasedApisMap.put(roleType, new HashSet()); + } + } + + @Override + public boolean checkAccess(User user, String commandName) throws PermissionDeniedException { + Account account = _accountService.getAccount(user.getAccountId()); + if (account == null) { + throw new PermissionDeniedException("The account id=" + user.getAccountId() + "for user id=" + user.getId() + + "is null"); + } + + List policies = _iamSrv.listIAMPolicies(account.getAccountId()); + + boolean isAllowed = _iamSrv.isActionAllowedForPolicies(commandName, policies); + if (!isAllowed) { + throw new PermissionDeniedException("The API does not exist or is blacklisted. api: " + commandName); + } + return isAllowed; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + super.configure(name, params); + + processMapping(PropertiesUtil.processConfigFile(new String[] { "commands.properties" })); + return true; + } + + @Override + public boolean start() { + + // drop all default policy api permissions - we reload them every time + // to include any changes done to the @APICommand or + // commands.properties. + + for (RoleType role : RoleType.values()) { + Long policyId = getDefaultPolicyId(role); + if (policyId != null) { + _iamSrv.resetIAMPolicy(policyId); + } + } + + // add the system-domain capability + + _iamSrv.addIAMPermissionToIAMPolicy(new Long(Account.ACCOUNT_TYPE_ADMIN + 1), null, null, null, + "SystemCapability", null, Permission.Allow, false); + _iamSrv.addIAMPermissionToIAMPolicy(new Long(Account.ACCOUNT_TYPE_DOMAIN_ADMIN + 1), null, null, null, + "DomainCapability", null, Permission.Allow, false); + _iamSrv.addIAMPermissionToIAMPolicy(new Long(Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN + 1), null, null, null, + "DomainResourceCapability", null, Permission.Allow, false); + + // add permissions for public templates + List pTmplts = _templateDao.listByPublic(); + for (VMTemplateVO tmpl : pTmplts){ + _iamSrv.addIAMPermissionToIAMPolicy(new Long(Account.ACCOUNT_TYPE_DOMAIN_ADMIN + 1), IAMEntityType.VirtualMachineTemplate.toString(), + PermissionScope.RESOURCE.toString(), tmpl.getId(), "listTemplates", AccessType.UseEntry.toString(), Permission.Allow, false); + _iamSrv.addIAMPermissionToIAMPolicy(new Long(Account.ACCOUNT_TYPE_NORMAL + 1), IAMEntityType.VirtualMachineTemplate.toString(), + PermissionScope.RESOURCE.toString(), tmpl.getId(), "listTemplates", AccessType.UseEntry.toString(), Permission.Allow, false); + } + + for (PluggableService service : _services) { + for (Class cmdClass : service.getCommands()) { + APICommand command = cmdClass.getAnnotation(APICommand.class); + if (!commandsPropertiesOverrides.contains(command.name())) { + for (RoleType role : command.authorized()) { + addDefaultAclPolicyPermission(command.name(), cmdClass, role); + } + } + } + } + + // read commands.properties and load api acl permissions - + // commands.properties overrides any @APICommand authorization + + for (String apiName : commandsPropertiesOverrides) { + Class cmdClass = _apiServer.getCmdClass(apiName); + for (RoleType role : RoleType.values()) { + if (commandsPropertiesRoleBasedApisMap.get(role).contains(apiName)) { + // insert permission for this role for this api + addDefaultAclPolicyPermission(apiName, cmdClass, role); + } + } + } + + return super.start(); + } + + private Long getDefaultPolicyId(RoleType role) { + Long policyId = null; + switch (role) { + case User: + policyId = new Long(Account.ACCOUNT_TYPE_NORMAL + 1); + break; + + case Admin: + policyId = new Long(Account.ACCOUNT_TYPE_ADMIN + 1); + break; + + case DomainAdmin: + policyId = new Long(Account.ACCOUNT_TYPE_DOMAIN_ADMIN + 1); + break; + + case ResourceAdmin: + policyId = new Long(Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN + 1); + break; + } + + return policyId; + } + + private void processMapping(Map configMap) { + for (Map.Entry entry : configMap.entrySet()) { + String apiName = entry.getKey(); + String roleMask = entry.getValue(); + commandsPropertiesOverrides.add(apiName); + try { + short cmdPermissions = Short.parseShort(roleMask); + for (RoleType roleType : RoleType.values()) { + if ((cmdPermissions & roleType.getValue()) != 0) + commandsPropertiesRoleBasedApisMap.get(roleType).add(apiName); + } + } catch (NumberFormatException nfe) { + s_logger.info("Malformed key=value pair for entry: " + entry.toString()); + } + } + } + + public List getServices() { + return _services; + } + + @Inject + public void setServices(List services) { + _services = services; + } + + private void addDefaultAclPolicyPermission(String apiName, Class cmdClass, RoleType role) { + + AccessType accessType = null; + IAMEntityType[] entityTypes = null; + if (cmdClass != null) { + BaseCmd cmdObj; + try { + cmdObj = (BaseCmd) cmdClass.newInstance(); + if (cmdObj instanceof BaseListCmd) { + accessType = AccessType.UseEntry; + } else if (!(cmdObj instanceof BaseAsyncCreateCmd)) { + accessType = AccessType.OperateEntry; + } + } catch (Exception e) { + throw new CloudRuntimeException(String.format( + "%s is claimed as an API command, but it cannot be instantiated", cmdClass.getName())); + } + + APICommand at = cmdClass.getAnnotation(APICommand.class); + entityTypes = at.entityType(); + } + + PermissionScope permissionScope = PermissionScope.ACCOUNT; + Long policyId = getDefaultPolicyId(role); + switch (role) { + case User: + permissionScope = PermissionScope.ACCOUNT; + break; + + case Admin: + permissionScope = PermissionScope.ALL; + break; + + case DomainAdmin: + permissionScope = PermissionScope.DOMAIN; + break; + + case ResourceAdmin: + permissionScope = PermissionScope.DOMAIN; + break; + } + + + if (entityTypes == null || entityTypes.length == 0) { + _iamSrv.addIAMPermissionToIAMPolicy(policyId, null, permissionScope.toString(), new Long(IAMPolicyPermission.PERMISSION_SCOPE_ID_CURRENT_CALLER), + apiName, (accessType == null) ? null : accessType.toString(), Permission.Allow, false); + } else { + for (IAMEntityType entityType : entityTypes) { + _iamSrv.addIAMPermissionToIAMPolicy(policyId, entityType.toString(), permissionScope.toString(), new Long(IAMPolicyPermission.PERMISSION_SCOPE_ID_CURRENT_CALLER), + apiName, (accessType == null) ? null : accessType.toString(), Permission.Allow, false); + } + } + + } + +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/iam/RoleBasedEntityAccessChecker.java b/services/iam/plugin/src/org/apache/cloudstack/iam/RoleBasedEntityAccessChecker.java new file mode 100644 index 00000000000..d0d9d88120e --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/iam/RoleBasedEntityAccessChecker.java @@ -0,0 +1,186 @@ +// 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.iam; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.acl.PermissionScope; +import org.apache.cloudstack.acl.SecurityChecker; +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.iam.api.IAMGroup; +import org.apache.cloudstack.iam.api.IAMPolicy; +import org.apache.cloudstack.iam.api.IAMPolicyPermission; +import org.apache.cloudstack.iam.api.IAMService; + +import com.cloud.acl.DomainChecker; +import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.user.Account; +import com.cloud.user.AccountService; + +public class RoleBasedEntityAccessChecker extends DomainChecker implements SecurityChecker { + + private static final Logger s_logger = Logger.getLogger(RoleBasedEntityAccessChecker.class.getName()); + + @Inject + AccountService _accountService; + + @Inject DomainDao _domainDao; + + @Inject + IAMService _iamSrv; + + + @Override + public boolean checkAccess(Account caller, ControlledEntity entity, AccessType accessType) + throws PermissionDeniedException { + return checkAccess(caller, entity, accessType, null); + } + + @Override + public boolean checkAccess(Account caller, ControlledEntity entity, AccessType accessType, String action) + throws PermissionDeniedException { + + if (entity == null && action != null) { + // check if caller can do this action + List policies = _iamSrv.listIAMPolicies(caller.getAccountId()); + + boolean isAllowed = _iamSrv.isActionAllowedForPolicies(action, policies); + if (!isAllowed) { + throw new PermissionDeniedException("The action '" + action + "' not allowed for account " + caller); + } + return true; + } + + String entityType = entity.getEntityType().toString(); + + if (accessType == null) { + accessType = AccessType.UseEntry; + } + + // get all Policies of this caller w.r.t the entity + List policies = getEffectivePolicies(caller, entity); + HashMap policyPermissionMap = new HashMap(); + + for (IAMPolicy policy : policies) { + List permissions = new ArrayList(); + + if (action != null) { + permissions = _iamSrv.listPolicyPermissionByActionAndEntity(policy.getId(), action, entityType); + if (permissions.isEmpty()) { + if (accessType != null) { + permissions.addAll(_iamSrv.listPolicyPermissionByAccessAndEntity(policy.getId(), + accessType.toString(), entityType)); + } + } + } else { + if (accessType != null) { + permissions.addAll(_iamSrv.listPolicyPermissionByAccessAndEntity(policy.getId(), + accessType.toString(), entityType)); + } + } + for (IAMPolicyPermission permission : permissions) { + if (checkPermissionScope(caller, permission.getScope(), permission.getScopeId(), entity)) { + if (permission.getEntityType().equals(entityType)) { + policyPermissionMap.put(policy, permission.getPermission().isGranted()); + break; + } else if (permission.getEntityType().equals("*")) { + policyPermissionMap.put(policy, permission.getPermission().isGranted()); + } + } + } + if (policyPermissionMap.containsKey(policy) && policyPermissionMap.get(policy)) { + return true; + } + } + + if (!policies.isEmpty()) { // Since we reach this point, none of the + // roles granted access + if (s_logger.isDebugEnabled()) { + s_logger.debug("Account " + caller + " does not have permission to access resource " + entity + + " for access type: " + accessType); + } + throw new PermissionDeniedException(caller + " does not have permission to access resource " + entity); + } + + return false; + } + + private boolean checkPermissionScope(Account caller, String scope, Long scopeId, ControlledEntity entity) { + + if(scopeId != null && !scopeId.equals(new Long(IAMPolicyPermission.PERMISSION_SCOPE_ID_CURRENT_CALLER))){ + //scopeId is set + if (scope.equals(PermissionScope.ACCOUNT.name())) { + if(scopeId == entity.getAccountId()){ + return true; + } + } else if (scope.equals(PermissionScope.DOMAIN.name())) { + if (_domainDao.isChildDomain(scopeId, entity.getDomainId())) { + return true; + } + } else if (scope.equals(PermissionScope.RESOURCE.name())) { + if (entity instanceof InternalIdentity) { + InternalIdentity entityWithId = (InternalIdentity) entity; + if(scopeId.equals(entityWithId.getId())){ + return true; + } + } + } + } else if (scopeId == null || scopeId.equals(new Long(IAMPolicyPermission.PERMISSION_SCOPE_ID_CURRENT_CALLER))) { + if (scope.equals(PermissionScope.ACCOUNT.name())) { + if(caller.getAccountId() == entity.getAccountId()){ + return true; + } + } else if (scope.equals(PermissionScope.DOMAIN.name())) { + if (_domainDao.isChildDomain(caller.getDomainId(), entity.getDomainId())) { + return true; + } + } + } + return false; + } + + private List getEffectivePolicies(Account caller, ControlledEntity entity) { + + // Get the static Policies of the Caller + List policies = _iamSrv.listIAMPolicies(caller.getId()); + + // add any dynamic policies w.r.t the entity + if (caller.getId() == entity.getAccountId()) { + // The caller owns the entity + policies.add(_iamSrv.getResourceOwnerPolicy()); + } + + List groups = _iamSrv.listIAMGroups(caller.getId()); + for (IAMGroup group : groups) { + // for each group find the grand parent groups. + List parentGroups = _iamSrv.listParentIAMGroups(group.getId()); + for (IAMGroup parentGroup : parentGroups) { + policies.addAll(_iamSrv.listRecursiveIAMPoliciesByGroup(parentGroup.getId())); + } + } + + return policies; + } +} diff --git a/services/iam/plugin/src/org/apache/cloudstack/iam/RoleBasedEntityQuerySelector.java b/services/iam/plugin/src/org/apache/cloudstack/iam/RoleBasedEntityQuerySelector.java new file mode 100644 index 00000000000..23c57a16880 --- /dev/null +++ b/services/iam/plugin/src/org/apache/cloudstack/iam/RoleBasedEntityQuerySelector.java @@ -0,0 +1,147 @@ +// 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.iam; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.acl.PermissionScope; +import org.apache.cloudstack.acl.QuerySelector; +import org.apache.cloudstack.iam.api.IAMGroup; +import org.apache.cloudstack.iam.api.IAMPolicy; +import org.apache.cloudstack.iam.api.IAMPolicyPermission; +import org.apache.cloudstack.iam.api.IAMService; + +import com.cloud.user.Account; +import com.cloud.utils.component.AdapterBase; + +public class RoleBasedEntityQuerySelector extends AdapterBase implements QuerySelector { + + private static final Logger s_logger = Logger.getLogger(RoleBasedEntityQuerySelector.class.getName()); + + @Inject + IAMService _iamService; + + @Override + public List getAuthorizedDomains(Account caller, String action) { + long accountId = caller.getAccountId(); + // Get the static Policies of the Caller + List policies = _iamService.listIAMPolicies(accountId); + // for each policy, find granted permission with Domain scope + List domainIds = new ArrayList(); + for (IAMPolicy policy : policies) { + List pp = _iamService.listPolicyPermissionsByScope(policy.getId(), action, PermissionScope.DOMAIN.toString()); + if (pp != null) { + for (IAMPolicyPermission p : pp) { + if (p.getScopeId() != null) { + if (p.getScopeId().longValue() == -1) { + domainIds.add(caller.getDomainId()); + } else { + domainIds.add(p.getScopeId()); + } + } + } + } + } + return domainIds; + } + + @Override + public List getAuthorizedAccounts(Account caller, String action) { + long accountId = caller.getAccountId(); + // Get the static Policies of the Caller + List policies = _iamService.listIAMPolicies(accountId); + // for each policy, find granted permission with Account scope + List accountIds = new ArrayList(); + for (IAMPolicy policy : policies) { + List pp = _iamService.listPolicyPermissionsByScope(policy.getId(), action, PermissionScope.ACCOUNT.toString()); + if (pp != null) { + for (IAMPolicyPermission p : pp) { + if (p.getScopeId() != null) { + if (p.getScopeId().longValue() == -1) { + accountIds.add(caller.getId()); + } else { + accountIds.add(p.getScopeId()); + } + } + } + } + } + return accountIds; + } + + @Override + public List getAuthorizedResources(Account caller, String action) { + long accountId = caller.getAccountId(); + // Get the static Policies of the Caller + List policies = _iamService.listIAMPolicies(accountId); + + // add the policies that grant recursive access + List groups = _iamService.listIAMGroups(caller.getId()); + for (IAMGroup group : groups) { + // for each group find the grand parent groups. + List parentGroups = _iamService.listParentIAMGroups(group.getId()); + for (IAMGroup parentGroup : parentGroups) { + policies.addAll(_iamService.listRecursiveIAMPoliciesByGroup(parentGroup.getId())); + } + } + + // for each policy, find granted permission with Resource scope + List entityIds = new ArrayList(); + for (IAMPolicy policy : policies) { + List pp = _iamService.listPolicyPermissionsByScope(policy.getId(), action, PermissionScope.RESOURCE.toString()); + if (pp != null) { + for (IAMPolicyPermission p : pp) { + if (p.getScopeId() != null) { + entityIds.add(p.getScopeId()); + } + } + } + } + return entityIds; + } + + @Override + public boolean isGrantedAll(Account caller, String action) { + long accountId = caller.getAccountId(); + // Get the static Policies of the Caller + List policies = _iamService.listIAMPolicies(accountId); + // for each policy, find granted permission with ALL scope + for (IAMPolicy policy : policies) { + List pp = _iamService.listPolicyPermissionsByScope(policy.getId(), action, PermissionScope.ALL.toString()); + if (pp != null && pp.size() > 0) { + return true; + } + } + return false; + } + + @Override + public List listAclGroupsByAccount(long accountId) { + List groups = _iamService.listIAMGroups(accountId); + List groupNames = new ArrayList(); + for (IAMGroup grp : groups) { + groupNames.add(grp.getName()); + } + return groupNames; + } + +} diff --git a/services/iam/plugin/test/org/apache/cloudstack/iam/test/IAMApiServiceTest.java b/services/iam/plugin/test/org/apache/cloudstack/iam/test/IAMApiServiceTest.java new file mode 100644 index 00000000000..dc5c1688d19 --- /dev/null +++ b/services/iam/plugin/test/org/apache/cloudstack/iam/test/IAMApiServiceTest.java @@ -0,0 +1,369 @@ +package org.apache.cloudstack.iam.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.TypeFilter; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import org.apache.cloudstack.acl.IAMEntityType; +import org.apache.cloudstack.acl.PermissionScope; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.api.command.user.vm.ListVMsCmd; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.iam.IAMGroupResponse; +import org.apache.cloudstack.api.response.iam.IAMPermissionResponse; +import org.apache.cloudstack.api.response.iam.IAMPolicyResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.messagebus.MessageBus; +import org.apache.cloudstack.iam.IAMApiService; +import org.apache.cloudstack.iam.IAMApiServiceImpl; +import org.apache.cloudstack.iam.api.IAMGroup; +import org.apache.cloudstack.iam.api.IAMPolicy; +import org.apache.cloudstack.iam.api.IAMPolicyPermission; +import org.apache.cloudstack.iam.api.IAMPolicyPermission.Permission; +import org.apache.cloudstack.iam.api.IAMService; +import org.apache.cloudstack.iam.server.IAMGroupVO; +import org.apache.cloudstack.iam.server.IAMPolicyPermissionVO; +import org.apache.cloudstack.iam.server.IAMPolicyVO; +import org.apache.cloudstack.test.utils.SpringUtils; + +import com.cloud.api.ApiServerService; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.network.dao.NetworkDomainDao; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.UserVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.Pair; +import com.cloud.utils.component.ComponentContext; +import com.cloud.utils.db.EntityManager; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(loader = AnnotationConfigContextLoader.class) +public class IAMApiServiceTest { + + @Inject + IAMService _iamSrv; + + @Inject + DomainDao _domainDao; + + @Inject + IAMApiService _aclSrv; + + @Inject + AccountManager _accountMgr; + + @Inject + AccountDao _accountDao; + + @Inject + ApiServerService _apiServer; + + private static Account caller; + private static Long callerId; + private static String callerAccountName = "tester"; + private static Long callerDomainId = 3L; + private static String callerDomainPath = "/root/testdomain"; + private static DomainVO callerDomain; + + @BeforeClass + public static void setUpClass() throws ConfigurationException { + } + + @Before + public void setUp() { + ComponentContext.initComponentsLifeCycle(); + caller = new AccountVO(callerAccountName, callerDomainId, null, Account.ACCOUNT_TYPE_ADMIN, UUID.randomUUID().toString()); + callerId = caller.getId(); + callerDomain = new DomainVO(); + callerDomain.setPath(callerDomainPath); + UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString()); + CallContext.register(user, caller); + + when(_domainDao.findById(callerDomainId)).thenReturn(callerDomain); + doNothing().when(_accountMgr).checkAccess(caller, callerDomain); + } + + @Test + public void createIAMGroupTest() { + IAMGroup group = new IAMGroupVO("group1", "tester group1"); + List groups = new ArrayList(); + groups.add(group); + Pair, Integer> grpList = new Pair, Integer>(groups, 1); + when(_iamSrv.createIAMGroup("group1", "tester group1", callerDomainPath)).thenReturn(group); + when(_iamSrv.listIAMGroups(null, null, callerDomainPath, 0L, 20L)).thenReturn(grpList); + + IAMGroup createdGrp = _aclSrv.createIAMGroup(caller, "group1", "tester group1"); + assertNotNull("IAM group 'group1' failed to create ", createdGrp); + ListResponse grpResp = _aclSrv.listIAMGroups(null, null, callerDomainId, 0L, 20L); + assertTrue("No. of response items should be one", grpResp.getCount() == 1); + IAMGroupResponse resp = grpResp.getResponses().get(0); + assertEquals("Error in created group name", "group1", resp.getName()); + } + + @Test + public void deleteIAMGroupTest() { + when(_iamSrv.deleteIAMGroup(1L)).thenReturn(true); + assertTrue("failed to delete acl group 1", _aclSrv.deleteIAMGroup(1L)); + } + + @Test + public void listIAMGroupTest() { + IAMGroup group = new IAMGroupVO("group1", "tester group1"); + List groups = new ArrayList(); + groups.add(group); + when(_iamSrv.listIAMGroups(callerId)).thenReturn(groups); + List grps = _aclSrv.listIAMGroups(callerId); + assertTrue(grps != null && grps.size() == 1); + IAMGroup grp = grps.get(0); + assertEquals("Error to retrieve group", "group1", grp.getName()); + } + + @Test + public void addRemoveAccountToGroupTest() { + IAMGroup group = new IAMGroupVO("group1", "tester group1"); + List groups = new ArrayList(); + groups.add(group); + Long groupId = group.getId(); + List acctIds = new ArrayList(); + AccountVO acct1 = new AccountVO(100L); + acct1.setAccountName("account1"); + AccountVO acct2 = new AccountVO(200L); + acct2.setAccountName("account2"); + acctIds.add(acct1.getId()); + acctIds.add(acct2.getId()); + when(_accountDao.findById(acct1.getId())).thenReturn(acct1); + when(_accountDao.findById(acct2.getId())).thenReturn(acct2); + when(_iamSrv.addAccountsToGroup(acctIds, groupId)).thenReturn(group); + when(_iamSrv.listAccountsByGroup(groupId)).thenReturn(acctIds); + Pair, Integer> grpList = new Pair, Integer>(groups, 1); + when(_iamSrv.listIAMGroups(null, "group1", callerDomainPath, 0L, 20L)).thenReturn(grpList); + _aclSrv.addAccountsToGroup(acctIds, groupId); + ListResponse grpResp = _aclSrv.listIAMGroups(null, "group1", callerDomainId, 0L, 20L); + assertTrue("No. of response items should be one", grpResp.getCount() == 1); + IAMGroupResponse resp = grpResp.getResponses().get(0); + Set acctNames = resp.getAccountNameList(); + assertEquals("There should be 2 accounts in the group", 2, acctNames.size()); + assertTrue("account1 should be assigned to the group", acctNames.contains("account1")); + assertTrue("account2 should be assigned to the group", acctNames.contains("account2")); + // remove "account2" from group1 + acctIds.remove(1); + List rmAccts = new ArrayList(); + rmAccts.add(acct2.getId()); + when(_iamSrv.removeAccountsFromGroup(rmAccts, groupId)).thenReturn(group); + _aclSrv.removeAccountsFromGroup(acctIds, groupId); + grpResp = _aclSrv.listIAMGroups(null, "group1", callerDomainId, 0L, 20L); + assertTrue("No. of response items should be one", grpResp.getCount() == 1); + resp = grpResp.getResponses().get(0); + acctNames = resp.getAccountNameList(); + assertEquals("There should be 1 accounts in the group", 1, acctNames.size()); + assertFalse("account2 should not belong to the group anymore", acctNames.contains("account2")); + } + + @Test + public void createIAMPolicyTest() { + IAMPolicy policy = new IAMPolicyVO("policy1", "tester policy1"); + List policies = new ArrayList(); + policies.add(policy); + Pair, Integer> policyList = new Pair, Integer>(policies, 1); + when(_iamSrv.createIAMPolicy("policy1", "tester policy1", null, callerDomainPath)).thenReturn(policy); + when(_iamSrv.listIAMPolicies(null, null, callerDomainPath, 0L, 20L)).thenReturn(policyList); + + IAMPolicy createdPolicy = _aclSrv.createIAMPolicy(caller, "policy1", "tester policy1", null); + assertNotNull("IAM policy 'policy1' failed to create ", createdPolicy); + ListResponse policyResp = _aclSrv.listIAMPolicies(null, null, callerDomainId, 0L, 20L); + assertTrue("No. of response items should be one", policyResp.getCount() == 1); + IAMPolicyResponse resp = policyResp.getResponses().get(0); + assertEquals("Error in created group name", "policy1", resp.getName()); + } + + @Test + public void deleteIAMPolicyTest() { + when(_iamSrv.deleteIAMPolicy(1L)).thenReturn(true); + assertTrue("failed to delete acl policy 1", _aclSrv.deleteIAMPolicy(1L)); + } + + @Test + public void listIAMPolicyTest() { + IAMPolicy policy = new IAMPolicyVO("policy1", "tester policy1"); + List policies = new ArrayList(); + policies.add(policy); + when(_iamSrv.listIAMPolicies(callerId)).thenReturn(policies); + List polys = _aclSrv.listIAMPolicies(callerId); + assertTrue(polys != null && polys.size() == 1); + IAMPolicy p = polys.get(0); + assertEquals("Error to retrieve group", "policy1", p.getName()); + } + + @Test + public void addRemovePolicyToGroupTest() { + IAMGroup group = new IAMGroupVO("group1", "tester group1"); + List groups = new ArrayList(); + groups.add(group); + Long groupId = group.getId(); + List policyIds = new ArrayList(); + policyIds.add(100L); + policyIds.add(200L); + IAMPolicy policy1 = new IAMPolicyVO("policy1", "my first policy"); + IAMPolicy policy2 = new IAMPolicyVO("policy2", "my second policy"); + List policies = new ArrayList(); + policies.add(policy1); + policies.add(policy2); + when(_iamSrv.attachIAMPoliciesToGroup(policyIds, groupId)).thenReturn(group); + when(_iamSrv.listIAMPoliciesByGroup(groupId)).thenReturn(policies); + Pair, Integer> grpList = new Pair, Integer>(groups, 1); + when(_iamSrv.listIAMGroups(null, "group1", callerDomainPath, 0L, 20L)).thenReturn(grpList); + _aclSrv.attachIAMPoliciesToGroup(policyIds, groupId); + ListResponse grpResp = _aclSrv.listIAMGroups(null, "group1", callerDomainId, 0L, 20L); + assertTrue("No. of response items should be one", grpResp.getCount() == 1); + IAMGroupResponse resp = grpResp.getResponses().get(0); + Set policyNames = resp.getPolicyList(); + assertEquals("There should be 2 policies in the group", 2, policyNames.size()); + assertTrue("policy1 should be assigned to the group", policyNames.contains("policy1")); + assertTrue("policy2 should be assigned to the group", policyNames.contains("policy2")); + // remove "policy2" from group1 + policyIds.remove(1); + policies.remove(policy2); + when(_iamSrv.removeIAMPoliciesFromGroup(policyIds, groupId)).thenReturn(group); + _aclSrv.removeIAMPoliciesFromGroup(policyIds, groupId); + grpResp = _aclSrv.listIAMGroups(null, "group1", callerDomainId, 0L, 20L); + assertTrue("No. of response items should be one", grpResp.getCount() == 1); + resp = grpResp.getResponses().get(0); + policyNames = resp.getPolicyList(); + assertEquals("There should be 1 policy attached to the group", 1, policyNames.size()); + assertFalse("policy2 should not belong to the group anymore", policyNames.contains("policy2")); + } + + @Test + public void addRemovePermissionToPolicyTest() { + IAMPolicy policy = new IAMPolicyVO("policy1", "tester policy1"); + List policies = new ArrayList(); + policies.add(policy); + Long policyId = policy.getId(); + Long resId = 200L; + Class clz = ListVMsCmd.class; + when(_apiServer.getCmdClass("listVirtualMachines")).thenReturn(clz); + when( + _iamSrv.addIAMPermissionToIAMPolicy(policyId, IAMEntityType.VirtualMachine.toString(), + PermissionScope.RESOURCE.toString(), resId, "listVirtualMachines", + AccessType.UseEntry.toString(), Permission.Allow, false)).thenReturn(policy); + _aclSrv.addIAMPermissionToIAMPolicy(policyId, IAMEntityType.VirtualMachine.toString(), + PermissionScope.RESOURCE, resId, "listVirtualMachines", Permission.Allow, false); + Pair, Integer> policyList = new Pair, Integer>(policies, 1); + List policyPerms = new ArrayList(); + IAMPolicyPermission perm = new IAMPolicyPermissionVO(policyId, "listVirtualMachines", + IAMEntityType.VirtualMachine.toString(), AccessType.UseEntry.toString(), + PermissionScope.RESOURCE.toString(), + resId, Permission.Allow, false); + policyPerms.add(perm); + when(_iamSrv.listIAMPolicies(null, "policy1", callerDomainPath, 0L, 20L)).thenReturn(policyList); + when(_iamSrv.listPolicyPermissions(policyId)).thenReturn(policyPerms); + ListResponse policyResp = _aclSrv.listIAMPolicies(null, "policy1", callerDomainId, 0L, 20L); + assertTrue("No. of response items should be one", policyResp.getCount() == 1); + IAMPolicyResponse resp = policyResp.getResponses().get(0); + Set permList = resp.getPermissionList(); + assertTrue("Permission list should not be empty", permList != null && permList.size() > 0); + IAMPermissionResponse permResp = permList.iterator().next(); + assertEquals("There should be one permission for listVirtualMachines", "listVirtualMachines", permResp.getAction()); + + //remove permission from policy + policyPerms.remove(perm); + _aclSrv.removeIAMPermissionFromIAMPolicy(policyId, IAMEntityType.VirtualMachine.toString(), + PermissionScope.RESOURCE, resId, "listVirtualMachines"); + policyResp = _aclSrv.listIAMPolicies(null, "policy1", callerDomainId, 0L, 20L); + assertTrue("No. of response items should be one", policyResp.getCount() == 1); + resp = policyResp.getResponses().get(0); + permList = resp.getPermissionList(); + assertTrue("Permission list should be empty", permList != null && permList.size() == 0); + } + + @After + public void tearDown() { + } + + @Configuration + @ComponentScan(basePackageClasses = {IAMApiServiceImpl.class}, includeFilters = {@Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)}, useDefaultFilters = false) + public static class TestConfiguration extends SpringUtils.CloudStackTestConfiguration { + + @Bean + public DomainDao domainDao() { + return Mockito.mock(DomainDao.class); + } + + @Bean + public IAMService iamService() { + return Mockito.mock(IAMService.class); + } + + @Bean + public AccountDao accountDao() { + return Mockito.mock(AccountDao.class); + } + + @Bean + public NetworkDomainDao networkDomainDao() { + return Mockito.mock(NetworkDomainDao.class); + } + + @Bean + public AccountManager accountManager() { + return Mockito.mock(AccountManager.class); + } + + @Bean + public MessageBus messageBus() { + return Mockito.mock(MessageBus.class); + } + + @Bean + public EntityManager entityMgr() { + return Mockito.mock(EntityManager.class); + } + + @Bean + public ApiServerService apiServerService() { + return Mockito.mock(ApiServerService.class); + } + + public static class Library implements TypeFilter { + + @Override + public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException { + ComponentScan cs = TestConfiguration.class.getAnnotation(ComponentScan.class); + return SpringUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs); + } + } + } +} diff --git a/services/iam/plugin/test/resources/db.properties b/services/iam/plugin/test/resources/db.properties new file mode 100644 index 00000000000..e1b5fe9a2c1 --- /dev/null +++ b/services/iam/plugin/test/resources/db.properties @@ -0,0 +1,75 @@ +# 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. + + +# management server clustering parameters, change cluster.node.IP to the machine IP address +# in which the management server(Tomcat) is running +cluster.node.IP=127.0.0.1 +cluster.servlet.port=9090 +region.id=1 + +# CloudStack database settings +db.cloud.username=cloud +db.cloud.password=cloud +db.root.password= +db.cloud.host=localhost +db.cloud.port=3306 +db.cloud.name=cloud + +# CloudStack database tuning parameters +db.cloud.maxActive=250 +db.cloud.maxIdle=30 +db.cloud.maxWait=10000 +db.cloud.autoReconnect=true +db.cloud.validationQuery=SELECT 1 +db.cloud.testOnBorrow=true +db.cloud.testWhileIdle=true +db.cloud.timeBetweenEvictionRunsMillis=40000 +db.cloud.minEvictableIdleTimeMillis=240000 +db.cloud.poolPreparedStatements=false +db.cloud.url.params=prepStmtCacheSize=517&cachePrepStmts=true&prepStmtCacheSqlLimit=4096 + +# usage database settings +db.usage.username=cloud +db.usage.password=cloud +db.usage.host=localhost +db.usage.port=3306 +db.usage.name=cloud_usage + +# usage database tuning parameters +db.usage.maxActive=100 +db.usage.maxIdle=30 +db.usage.maxWait=10000 +db.usage.autoReconnect=true + +# awsapi database settings +db.awsapi.username=cloud +db.awsapi.password=cloud +db.awsapi.host=localhost +db.awsapi.port=3306 +db.awsapi.name=cloudbridge + +# Simulator database settings +db.simulator.username=cloud +db.simulator.password=cloud +db.simulator.host=localhost +db.simulator.port=3306 +db.simulator.name=simulator +db.simulator.maxActive=250 +db.simulator.maxIdle=30 +db.simulator.maxWait=10000 +db.simulator.autoReconnect=true