// 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.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.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;
import com.cloud.vm.VirtualMachine;
@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<IAMGroup> groups = new ArrayList<IAMGroup>();
groups.add(group);
Pair<List<IAMGroup>, Integer> grpList = new Pair<List<IAMGroup>, 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<IAMGroupResponse> 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<IAMGroup> groups = new ArrayList<IAMGroup>();
groups.add(group);
when(_iamSrv.listIAMGroups(callerId)).thenReturn(groups);
List<IAMGroup> 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<IAMGroup> groups = new ArrayList<IAMGroup>();
groups.add(group);
Long groupId = group.getId();
List<Long> acctIds = new ArrayList<Long>();
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<List<IAMGroup>, Integer> grpList = new Pair<List<IAMGroup>, Integer>(groups, 1);
when(_iamSrv.listIAMGroups(null, "group1", callerDomainPath, 0L, 20L)).thenReturn(grpList);
_aclSrv.addAccountsToGroup(acctIds, groupId);
ListResponse<IAMGroupResponse> 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<String> 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<Long> rmAccts = new ArrayList<Long>();
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<IAMPolicy> policies = new ArrayList<IAMPolicy>();
policies.add(policy);
Pair<List<IAMPolicy>, Integer> policyList = new Pair<List<IAMPolicy>, 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<IAMPolicyResponse> 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<IAMPolicy> policies = new ArrayList<IAMPolicy>();
policies.add(policy);
when(_iamSrv.listIAMPolicies(callerId)).thenReturn(policies);
List<IAMPolicy> 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<IAMGroup> groups = new ArrayList<IAMGroup>();
groups.add(group);
Long groupId = group.getId();
List<Long> policyIds = new ArrayList<Long>();
policyIds.add(100L);
policyIds.add(200L);
IAMPolicy policy1 = new IAMPolicyVO("policy1", "my first policy");
IAMPolicy policy2 = new IAMPolicyVO("policy2", "my second policy");
List<IAMPolicy> policies = new ArrayList<IAMPolicy>();
policies.add(policy1);
policies.add(policy2);
when(_iamSrv.attachIAMPoliciesToGroup(policyIds, groupId)).thenReturn(group);
when(_iamSrv.listIAMPoliciesByGroup(groupId)).thenReturn(policies);
Pair<List<IAMGroup>, Integer> grpList = new Pair<List<IAMGroup>, Integer>(groups, 1);
when(_iamSrv.listIAMGroups(null, "group1", callerDomainPath, 0L, 20L)).thenReturn(grpList);
_aclSrv.attachIAMPoliciesToGroup(policyIds, groupId);
ListResponse<IAMGroupResponse> 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<String> 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<IAMPolicy> policies = new ArrayList<IAMPolicy>();
policies.add(policy);
Long policyId = policy.getId();
Long resId = 200L;
Class clz = ListVMsCmd.class;
when(_apiServer.getCmdClass("listVirtualMachines")).thenReturn(clz);
when(
_iamSrv.addIAMPermissionToIAMPolicy(policyId, VirtualMachine.class.getSimpleName(),
PermissionScope.RESOURCE.toString(), resId, "listVirtualMachines",
AccessType.UseEntry.toString(), Permission.Allow, false)).thenReturn(policy);
_aclSrv.addIAMPermissionToIAMPolicy(policyId, VirtualMachine.class.getSimpleName(),
PermissionScope.RESOURCE, resId, "listVirtualMachines", Permission.Allow, false, false);
Pair<List<IAMPolicy>, Integer> policyList = new Pair<List<IAMPolicy>, Integer>(policies, 1);
List<IAMPolicyPermission> policyPerms = new ArrayList<IAMPolicyPermission>();
IAMPolicyPermission perm = new IAMPolicyPermissionVO(policyId, "listVirtualMachines",
VirtualMachine.class.getSimpleName(), 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<IAMPolicyResponse> 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<IAMPermissionResponse> 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, VirtualMachine.class.getSimpleName(),
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);
}
}
}
}