// 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.inject.Inject; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import org.bouncycastle.util.IPAddress; import com.amazonaws.auth.policy.Condition; import com.amazonaws.services.ec2.model.SecurityGroup; import com.amazonaws.services.ec2.model.Snapshot; import com.amazonaws.services.ec2.model.Volume; import com.amazonaws.services.ec2.model.Vpc; import com.amazonaws.services.elasticache.model.Event; import org.apache.cloudstack.acl.PermissionScope; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.affinity.AffinityGroup; 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.AsyncJob; 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 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.exception.InvalidParameterValueException; import com.cloud.network.IpAddress; import com.cloud.network.MonitoringService; import com.cloud.network.Network; import com.cloud.network.RemoteAccessVpn; import com.cloud.network.Site2SiteCustomerGateway; import com.cloud.network.Site2SiteVpnConnection; import com.cloud.network.Site2SiteVpnGateway; import com.cloud.network.UserIpv6Address; import com.cloud.network.VpnUser; import com.cloud.network.as.AutoScalePolicy; import com.cloud.network.as.AutoScaleVmGroup; import com.cloud.network.as.AutoScaleVmProfile; import com.cloud.network.lb.SslCert; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.PortForwardingRule; import com.cloud.network.vpc.StaticRoute; import com.cloud.network.vpc.VpcGateway; import com.cloud.projects.ProjectInvitation; import com.cloud.region.ha.GlobalLoadBalancerRule; import com.cloud.server.ResourceTag; import com.cloud.template.TemplateManager; import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; import com.cloud.user.DomainManager; import com.cloud.user.SSHKeyPair; 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.InstanceGroup; import com.cloud.vm.NicIpAlias; import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.VirtualMachine; import com.cloud.vm.snapshot.VMSnapshot; 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<String, Class<?>> s_typeMap = new HashMap<String, Class<?>>(); static { s_typeMap.put(VirtualMachine.class.getSimpleName(), VirtualMachine.class); s_typeMap.put(Volume.class.getSimpleName(), Volume.class); s_typeMap.put(ResourceTag.class.getSimpleName(), ResourceTag.class); s_typeMap.put(Account.class.getSimpleName(), Account.class); s_typeMap.put(AffinityGroup.class.getSimpleName(), AffinityGroup.class); s_typeMap.put(AutoScalePolicy.class.getSimpleName(), AutoScalePolicy.class); s_typeMap.put(AutoScaleVmProfile.class.getSimpleName(), AutoScaleVmProfile.class); s_typeMap.put(AutoScaleVmGroup.class.getSimpleName(), AutoScaleVmGroup.class); s_typeMap.put(Condition.class.getSimpleName(), Condition.class); s_typeMap.put(Vpc.class.getSimpleName(), Vpc.class); s_typeMap.put(VpcGateway.class.getSimpleName(), VpcGateway.class); s_typeMap.put(VpnUser.class.getSimpleName(), VpnUser.class); s_typeMap.put(VMSnapshot.class.getSimpleName(), VMSnapshot.class); s_typeMap.put(VirtualMachineTemplate.class.getSimpleName(), VirtualMachineTemplate.class); s_typeMap.put(UserIpv6Address.class.getSimpleName(), UserIpv6Address.class); s_typeMap.put(StaticRoute.class.getSimpleName(), StaticRoute.class); s_typeMap.put(SSHKeyPair.class.getSimpleName(), SSHKeyPair.class); s_typeMap.put(Snapshot.class.getSimpleName(), Snapshot.class); s_typeMap.put(Site2SiteVpnGateway.class.getSimpleName(), Site2SiteVpnGateway.class); s_typeMap.put(Site2SiteCustomerGateway.class.getSimpleName(), Site2SiteCustomerGateway.class); s_typeMap.put(Site2SiteVpnConnection.class.getSimpleName(), Site2SiteVpnConnection.class); s_typeMap.put(SecurityGroup.class.getSimpleName(), SecurityGroup.class); s_typeMap.put(RemoteAccessVpn.class.getSimpleName(), RemoteAccessVpn.class); s_typeMap.put(ProjectInvitation.class.getSimpleName(), ProjectInvitation.class); s_typeMap.put(NicSecondaryIp.class.getSimpleName(), NicSecondaryIp.class); s_typeMap.put(NicIpAlias.class.getSimpleName(), NicIpAlias.class); s_typeMap.put(Network.class.getSimpleName(), Network.class); s_typeMap.put(IpAddress.class.getSimpleName(), IPAddress.class); s_typeMap.put(InstanceGroup.class.getSimpleName(), InstanceGroup.class); s_typeMap.put(GlobalLoadBalancerRule.class.getSimpleName(), GlobalLoadBalancerRule.class); s_typeMap.put(FirewallRule.class.getSimpleName(), FirewallRule.class); s_typeMap.put(PortForwardingRule.class.getSimpleName(), PortForwardingRule.class); s_typeMap.put(Event.class.getSimpleName(), Event.class); s_typeMap.put(AsyncJob.class.getSimpleName(), AsyncJob.class); s_typeMap.put(IAMGroup.class.getSimpleName(), IAMGroup.class); s_typeMap.put(IAMPolicy.class.getSimpleName(), IAMPolicy.class); s_typeMap.put(MonitoringService.class.getSimpleName(), MonitoringService.class); s_typeMap.put(SslCert.class.getSimpleName(), SslCert.class); } @Override public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException { _messageBus.subscribe(AccountManager.MESSAGE_ADD_ACCOUNT_EVENT, new MessageSubscriber() { @Override public void onPublishMessage(String senderAddress, String subject, Object obj) { HashMap<Long, Long> acctGroupMap = (HashMap<Long, Long>) 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<IAMGroup> 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<IAMGroup> 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 default root admin, domain admin and normal user policies"); _iamSrv.addIAMPermissionToIAMPolicy(new Long(Account.ACCOUNT_TYPE_ADMIN + 1), VirtualMachineTemplate.class.getSimpleName(), PermissionScope.RESOURCE.toString(), templateId, "listTemplates", AccessType.UseEntry.toString(), Permission.Allow, false); _iamSrv.addIAMPermissionToIAMPolicy(new Long(Account.ACCOUNT_TYPE_DOMAIN_ADMIN + 1), VirtualMachineTemplate.class.getSimpleName(), PermissionScope.RESOURCE.toString(), templateId, "listTemplates", AccessType.UseEntry.toString(), Permission.Allow, false); _iamSrv.addIAMPermissionToIAMPolicy(new Long(Account.ACCOUNT_TYPE_NORMAL + 1), VirtualMachineTemplate.class.getSimpleName(), 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<Class<?>, Long> entity = (Pair<Class<?>, Long>)obj; if (entity != null) { String entityType = entity.first().getSimpleName(); 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<String, Object> permit = (Map<String, Object>)obj; if (permit != null) { Class<?> entityType = (Class<?>)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<Long> acctIds = (List<Long>)permit.get(ApiConstants.ACCOUNTS); s_logger.debug("MessageBus message: grant accounts permission to an entity: (" + entityType + "," + entityId + ")"); grantEntityPermissioinToAccounts(entityType.getSimpleName(), entityId, accessType, action, acctIds); } } }); _messageBus.subscribe(EntityManager.MESSAGE_REVOKE_ENTITY_EVENT, new MessageSubscriber() { @Override public void onPublishMessage(String senderAddress, String subject, Object obj) { Map<String, Object> permit = (Map<String, Object>)obj; if (permit != null) { Class<?> entityType = (Class<?>)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<Long> acctIds = (List<Long>)permit.get(ApiConstants.ACCOUNTS); s_logger.debug("MessageBus message: revoke from accounts permission to an entity: (" + entityType + "," + entityId + ")"); revokeEntityPermissioinFromAccounts(entityType.getSimpleName(), 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<String, Object> params = (Map<String, Object>) obj; if (params != null) { addDomainWideResourceAccess(params); } } }); return super.configure(name, params); } @Override public boolean start() { s_logger.info("Populating IAM group and account association for default accounts..."); // populate group <-> account association if not present for CS admin // and system accounts populateIAMGroupAdminAccountMap(); return true; } private void populateIAMGroupAdminAccountMap() { List<Long> sysAccts = new ArrayList<Long>(); sysAccts.add(Account.ACCOUNT_ID_SYSTEM); sysAccts.add(Account.ACCOUNT_ID_SYSTEM + 1); _iamSrv.addAccountsToGroup(sysAccts, new Long(Account.ACCOUNT_TYPE_ADMIN + 1)); } private void addDomainWideResourceAccess(Map<String, Object> params) { Class<?> entityType = (Class<?>)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 == Network.class) { createPolicyAndAddToDomainGroup("DomainWideNetwork-" + entityId, "domain wide network", entityType.toString(), entityId, "listNetworks", AccessType.UseEntry, domainId, isRecursive); } else if (entityType == AffinityGroup.class) { 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<Long> policyList = new ArrayList<Long>(); policyList.add(new Long(policy.getId())); List<IAMGroup> 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<IAMGroup> 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<Long> acctIds, final Long groupId) { return _iamSrv.addAccountsToGroup(acctIds, groupId); } private void removeAccountFromIAMGroups(long accountId) { List<IAMGroup> groups = listIAMGroups(accountId); List<Long> accts = new ArrayList<Long>(); accts.add(accountId); if (groups != null) { for (IAMGroup grp : groups) { removeAccountsFromGroup(accts, grp.getId()); } } } private void addAccountToIAMGroup(long accountId, long groupId) { List<Long> accts = new ArrayList<Long>(); 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<Long> 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<IAMPolicy> 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<Long> 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<Long> 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<Long> 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<Long> 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, Boolean readOnly) { Class<?> cmdClass = _apiServer.getCmdClass(action); AccessType accessType = null; if (BaseListCmd.class.isAssignableFrom(cmdClass)) { if (readOnly) { accessType = AccessType.ListEntry; } else { accessType = AccessType.UseEntry; } } else { accessType = AccessType.OperateEntry; } String accessTypeStr = (accessType != null) ? accessType.toString() : null; return _iamSrv.addIAMPermissionToIAMPolicy(iamPolicyId, entityType, scope.toString(), scopeId, action, accessTypeStr, 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<IAMPolicy> policies = _iamSrv.listIAMPolicies(accountId); IAMPolicyPermission curPerm = null; for (IAMPolicy policy : policies) { List<IAMPolicyPermission> 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<IAMPolicyPermission> 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(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<Long> 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<IAMPolicy> 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<IAMGroup> listDomainGroup(Domain domain) { if (domain != null) { String domainPath = domain.getPath(); // search for groups Pair<List<IAMGroup>, Integer> result = _iamSrv.listIAMGroups(null, "DomainGrp-" + domain.getUuid(), domainPath, null, null); return result.first(); } return new ArrayList<IAMGroup>(); } @Override public ListResponse<IAMGroupResponse> 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<List<IAMGroup>, Integer> result = _iamSrv.listIAMGroups(iamGroupId, iamGroupName, domainPath, startIndex, pageSize); // generate group response ListResponse<IAMGroupResponse> response = new ListResponse<IAMGroupResponse>(); List<IAMGroupResponse> groupResponses = new ArrayList<IAMGroupResponse>(); for (IAMGroup group : result.first()) { IAMGroupResponse resp = createIAMGroupResponse(group); groupResponses.add(resp); } response.setResponses(groupResponses, result.second()); return response; } @Override public ListResponse<IAMPolicyResponse> 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<List<IAMPolicy>, Integer> result = _iamSrv.listIAMPolicies(iamPolicyId, iamPolicyName, domainPath, startIndex, pageSize); // generate policy response ListResponse<IAMPolicyResponse> response = new ListResponse<IAMPolicyResponse>(); List<IAMPolicyResponse> policyResponses = new ArrayList<IAMPolicyResponse>(); 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<Long> 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, 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<Long> 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<IAMPolicy> 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), VirtualMachineTemplate.class.getSimpleName(), PermissionScope.RESOURCE.toString(), templateId, "listTemplates"); _iamSrv.removeIAMPermissionFromIAMPolicy(new Long(Account.ACCOUNT_TYPE_NORMAL + 1), VirtualMachineTemplate.class.getSimpleName(), PermissionScope.RESOURCE.toString(), templateId, "listTemplates"); // check if there is a policy with only UseEntry permission for this template added IAMPolicy policy = _iamSrv.getResourceGrantPolicy(VirtualMachineTemplate.class.getSimpleName(), 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(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<Class<?>> getCommands() { List<Class<?>> cmdList = new ArrayList<Class<?>>(); 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; } }