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