diff --git a/api/pom.xml b/api/pom.xml index 6352e117bfe..ce90eea4548 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -56,6 +56,11 @@ cloud-framework-ca ${project.version} + + org.apache.commons + commons-lang3 + ${cs.commons-lang3.version} + diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 26b692205bf..b7454693a74 100644 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -198,6 +198,7 @@ public class EventTypes { public static final String EVENT_USER_CREATE = "USER.CREATE"; public static final String EVENT_USER_DELETE = "USER.DELETE"; public static final String EVENT_USER_DISABLE = "USER.DISABLE"; + public static final String EVENT_USER_MOVE = "USER.MOVE"; public static final String EVENT_USER_UPDATE = "USER.UPDATE"; public static final String EVENT_USER_ENABLE = "USER.ENABLE"; public static final String EVENT_USER_LOCK = "USER.LOCK"; diff --git a/api/src/org/apache/cloudstack/api/command/admin/user/MoveUserCmd.java b/api/src/org/apache/cloudstack/api/command/admin/user/MoveUserCmd.java new file mode 100644 index 00000000000..b32aa2f1326 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/user/MoveUserCmd.java @@ -0,0 +1,126 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.user; + +import com.cloud.user.Account; +import com.cloud.user.User; +import com.google.common.base.Preconditions; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +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.UserResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.region.RegionService; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.log4j.Logger; + +import javax.inject.Inject; + +@APICommand(name = "moveUser", + description = "Moves a user to another account", + responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.11", + authorized = {RoleType.Admin}) +public class MoveUserCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(UpdateUserCmd.class.getName()); + + public static final String APINAME = "moveUser"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = UserResponse.class, + required = true, + description = "id of the user to be deleted") + private Long id; + + @Parameter(name = ApiConstants.ACCOUNT, + type = CommandType.STRING, + description = "Creates the user under the specified account. If no account is specified, the username will be used as the account name.") + private String accountName; + + @Parameter(name = ApiConstants.ACCOUNT_ID, + type = CommandType.UUID, + entityType = AccountResponse.class, + description = "Creates the user under the specified domain. Has to be accompanied with the account parameter") + private Long accountId; + + @Inject + RegionService _regionService; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getAccountName() { + return accountName; + } + + public Long getAccountId() { + return accountId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + } + + @Override + public long getEntityOwnerId() { + User user = _entityMgr.findById(User.class, getId()); + if (user != null) { + return user.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + @Override + public void execute() { + Preconditions.checkNotNull(getId(),"I have to have an user to move!"); + Preconditions.checkState(ObjectUtils.anyNotNull(getAccountId(),getAccountName()),"provide either an account name or an account id!"); + + CallContext.current().setEventDetails("UserId: " + getId()); + boolean result = + _regionService.moveUser(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to move the user to a new account"); + } + } + +} diff --git a/api/src/org/apache/cloudstack/region/RegionService.java b/api/src/org/apache/cloudstack/region/RegionService.java index afefcc7672e..bee66910b4d 100644 --- a/api/src/org/apache/cloudstack/region/RegionService.java +++ b/api/src/org/apache/cloudstack/region/RegionService.java @@ -27,6 +27,7 @@ import org.apache.cloudstack.api.command.admin.domain.UpdateDomainCmd; import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd; import org.apache.cloudstack.api.command.admin.user.DisableUserCmd; import org.apache.cloudstack.api.command.admin.user.EnableUserCmd; +import org.apache.cloudstack.api.command.admin.user.MoveUserCmd; import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; import org.apache.cloudstack.api.command.user.region.ListRegionsCmd; @@ -110,10 +111,17 @@ public interface RegionService { */ boolean deleteUser(DeleteUserCmd deleteUserCmd); + /** + * Deletes user by Id + * @param moveUserCmd + * @return true if delete was successful, false otherwise + */ + boolean moveUser(MoveUserCmd moveUserCmd); + /** * update an existing domain * - * @param cmd + * @param updateDomainCmd * - the command containing domainId and new domainName * @return Domain object if the command succeeded */ diff --git a/engine/schema/src/com/cloud/user/UserVO.java b/engine/schema/src/com/cloud/user/UserVO.java index d6ddb587204..05655bf711e 100644 --- a/engine/schema/src/com/cloud/user/UserVO.java +++ b/engine/schema/src/com/cloud/user/UserVO.java @@ -127,7 +127,25 @@ public class UserVO implements User, Identity, InternalIdentity { this.source = source; } - @Override + public UserVO(UserVO user) { + this.setAccountId(user.getAccountId()); + this.setUsername(user.getUsername()); + this.setPassword(user.getPassword()); + this.setFirstname(user.getFirstname()); + this.setLastname(user.getLastname()); + this.setEmail(user.getEmail()); + this.setTimezone(user.getTimezone()); + this.setUuid(user.getUuid()); + this.setSource(user.getSource()); + this.setApiKey(user.getApiKey()); + this.setSecretKey(user.getSecretKey()); + this.setExternalEntity(user.getExternalEntity()); + this.setRegistered(user.isRegistered()); + this.setRegistrationToken(user.getRegistrationToken()); + this.setState(user.getState()); + } + + @Override public long getId() { return id; } diff --git a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java index a6170131292..fa933244a66 100644 --- a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java +++ b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java @@ -25,6 +25,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd; +import org.apache.cloudstack.api.command.admin.user.MoveUserCmd; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.log4j.Logger; @@ -316,6 +317,10 @@ public class MockAccountManager extends ManagerBase implements AccountManager { return false; } + @Override public boolean moveUser(MoveUserCmd moveUserCmd) { + return false; + } + @Override public boolean deleteUserAccount(long arg0) { // TODO Auto-generated method stub diff --git a/pom.xml b/pom.xml index 66cfd0992a2..bc76817c0d8 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ 1.11.213 2.9.2 2.6 - 3.4 + 3.6 2.6 1.3.3 4.1 diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 177342b0575..c855c34b60f 100644 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -229,6 +229,7 @@ import org.apache.cloudstack.api.command.admin.user.GetUserCmd; import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd; import org.apache.cloudstack.api.command.admin.user.ListUsersCmd; import org.apache.cloudstack.api.command.admin.user.LockUserCmd; +import org.apache.cloudstack.api.command.admin.user.MoveUserCmd; import org.apache.cloudstack.api.command.admin.user.RegisterCmd; import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; @@ -2672,6 +2673,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(GetUserCmd.class); cmdList.add(ListUsersCmd.class); cmdList.add(LockUserCmd.class); + cmdList.add(MoveUserCmd.class); cmdList.add(RegisterCmd.class); cmdList.add(UpdateUserCmd.class); cmdList.add(CreateVlanIpRangeCmd.class); diff --git a/server/src/com/cloud/user/AccountManager.java b/server/src/com/cloud/user/AccountManager.java index 9e0dde20868..e0e7d3bf12d 100644 --- a/server/src/com/cloud/user/AccountManager.java +++ b/server/src/com/cloud/user/AccountManager.java @@ -23,6 +23,7 @@ import java.net.InetAddress; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd; import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd; +import org.apache.cloudstack.api.command.admin.user.MoveUserCmd; import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; import com.cloud.api.query.vo.ControlledViewEntity; @@ -155,10 +156,17 @@ public interface AccountManager extends AccountService, Configurable{ */ boolean deleteUser(DeleteUserCmd deleteUserCmd); + /** + * moves a user to another account within the same domain + * @param moveUserCmd + * @return true if the user was successfully moved + */ + boolean moveUser(MoveUserCmd moveUserCmd); + /** * Update a user by userId * - * @param userId + * @param cmd * @return UserAccount object */ UserAccount updateUser(UpdateUserCmd cmd); diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 294bc6e84ef..aaaa92ba3c1 100644 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -52,6 +52,7 @@ import org.apache.cloudstack.affinity.dao.AffinityGroupDao; import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd; import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd; import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd; +import org.apache.cloudstack.api.command.admin.user.MoveUserCmd; import org.apache.cloudstack.api.command.admin.user.RegisterCmd; import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; import org.apache.cloudstack.context.CallContext; @@ -1686,29 +1687,89 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Override @ActionEvent(eventType = EventTypes.EVENT_USER_DELETE, eventDescription = "deleting User") public boolean deleteUser(DeleteUserCmd deleteUserCmd) { - long id = deleteUserCmd.getId(); - - UserVO user = _userDao.findById(id); - - if (user == null) { - throw new InvalidParameterValueException("The specified user doesn't exist in the system"); - } + UserVO user = getValidUserVO(deleteUserCmd.getId()); Account account = _accountDao.findById(user.getAccountId()); + // don't allow to delete the user from the account of type Project + checkAccountAndAccess(user, account); + return _userDao.remove(deleteUserCmd.getId()); + } + + @ActionEvent(eventType = EventTypes.EVENT_USER_MOVE, eventDescription = "moving User to a new account") + public boolean moveUser(MoveUserCmd cmd) { + UserVO user = getValidUserVO(cmd.getId()); + Account oldAccount = _accountDao.findById(user.getAccountId()); + checkAccountAndAccess(user, oldAccount); + long domainId = oldAccount.getDomainId(); + + long newAccountId = getNewAccountId(cmd, domainId); + + if(newAccountId == user.getAccountId()) { + // could do a not silent fail but the objective of the user is reached + return true; // no need to create a new user object for this user + } + return Transaction.execute(new TransactionCallback() { + @Override + public Boolean doInTransaction(TransactionStatus status) { + UserVO newUser = new UserVO(user); + user.setExternalEntity(user.getUuid()); + user.setUuid(UUID.randomUUID().toString()); + _userDao.update(user.getId(),user); + newUser.setAccountId(newAccountId); + boolean success = _userDao.remove(cmd.getId()); + UserVO persisted = _userDao.persist(newUser); + return success && persisted.getUuid().equals(user.getExternalEntity()); + } + }); + } + + private long getNewAccountId(MoveUserCmd cmd, long domainId) { + Account newAccount = null; + if (StringUtils.isNotBlank(cmd.getAccountName())) { + if(s_logger.isDebugEnabled()) { + s_logger.debug("Getting id for account by name '" + cmd.getAccountName() + "' in domain " + domainId); + } + newAccount = _accountDao.findEnabledAccount(cmd.getAccountName(), domainId); + } + if (newAccount == null && cmd.getAccountId() != null) { + newAccount = _accountDao.findById(cmd.getAccountId()); + } + if (newAccount == null) { + throw new CloudRuntimeException("no account name or account id. this should have been caught before this point"); + } + long newAccountId = newAccount.getAccountId(); + + if(newAccount.getDomainId() != domainId) { + // not in scope + throw new InvalidParameterValueException("moving a user from an account in one domain to an account in annother domain is not supported!"); + } + return newAccountId; + } + + private void checkAccountAndAccess(UserVO user, Account account) { // don't allow to delete the user from the account of type Project if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) { + throw new InvalidParameterValueException("Project users cannot be deleted or moved."); + } + + checkAccess(CallContext.current().getCallingAccount(), AccessType.OperateEntry, true, account); + CallContext.current().putContextParameter(User.class, user.getUuid()); + } + + private UserVO getValidUserVO(long id) { + UserVO user = _userDao.findById(id); + + if (user == null || user.getRemoved() != null) { throw new InvalidParameterValueException("The specified user doesn't exist in the system"); } // don't allow to delete default user (system and admin users) if (user.isDefault()) { - throw new InvalidParameterValueException("The user is default and can't be removed"); + throw new InvalidParameterValueException("The user is default and can't be (re)moved"); } - checkAccess(CallContext.current().getCallingAccount(), AccessType.OperateEntry, true, account); - CallContext.current().putContextParameter(User.class, user.getUuid()); - return _userDao.remove(id); + return user; } protected class AccountCleanupTask extends ManagedContextRunnable { diff --git a/server/src/org/apache/cloudstack/region/RegionManager.java b/server/src/org/apache/cloudstack/region/RegionManager.java index 6f254817103..f7d7c10c5b9 100644 --- a/server/src/org/apache/cloudstack/region/RegionManager.java +++ b/server/src/org/apache/cloudstack/region/RegionManager.java @@ -21,6 +21,7 @@ import java.util.List; import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd; import org.apache.cloudstack.api.command.admin.domain.UpdateDomainCmd; import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd; +import org.apache.cloudstack.api.command.admin.user.MoveUserCmd; import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; import com.cloud.domain.Domain; @@ -122,10 +123,17 @@ public interface RegionManager { */ boolean deleteUser(DeleteUserCmd deleteUserCmd); + /** + * Deletes user by Id + * @param moveUserCmd + * @return + */ + boolean moveUser(MoveUserCmd moveUserCmd); + /** * update an existing domain * - * @param cmd + * @param updateDomainCmd * - the command containing domainId and new domainName * @return Domain object if the command succeeded */ @@ -142,7 +150,7 @@ public interface RegionManager { /** * Update a user by userId * - * @param userId + * @param updateUserCmd * @return UserAccount object */ UserAccount updateUser(UpdateUserCmd updateUserCmd); @@ -150,7 +158,7 @@ public interface RegionManager { /** * Disables a user by userId * - * @param userId + * @param id * - the userId * @return UserAccount object */ diff --git a/server/src/org/apache/cloudstack/region/RegionManagerImpl.java b/server/src/org/apache/cloudstack/region/RegionManagerImpl.java index 7e7189e09ee..0878eef8e8a 100644 --- a/server/src/org/apache/cloudstack/region/RegionManagerImpl.java +++ b/server/src/org/apache/cloudstack/region/RegionManagerImpl.java @@ -24,6 +24,7 @@ import java.util.Properties; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.api.command.admin.user.MoveUserCmd; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -224,6 +225,14 @@ public class RegionManagerImpl extends ManagerBase implements RegionManager, Man return _accountMgr.deleteUser(cmd); } + /** + * {@inheritDoc} + */ + @Override + public boolean moveUser(MoveUserCmd cmd) { + return _accountMgr.moveUser(cmd); + } + /** * {@inheritDoc} */ diff --git a/server/src/org/apache/cloudstack/region/RegionServiceImpl.java b/server/src/org/apache/cloudstack/region/RegionServiceImpl.java index cd3a147a9ac..5afafffc5da 100644 --- a/server/src/org/apache/cloudstack/region/RegionServiceImpl.java +++ b/server/src/org/apache/cloudstack/region/RegionServiceImpl.java @@ -34,6 +34,7 @@ import org.apache.cloudstack.api.command.admin.domain.UpdateDomainCmd; import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd; import org.apache.cloudstack.api.command.admin.user.DisableUserCmd; import org.apache.cloudstack.api.command.admin.user.EnableUserCmd; +import org.apache.cloudstack.api.command.admin.user.MoveUserCmd; import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; import org.apache.cloudstack.api.command.user.region.ListRegionsCmd; @@ -151,6 +152,14 @@ public class RegionServiceImpl extends ManagerBase implements RegionService, Man return _regionMgr.deleteUser(cmd); } + /** + * {@inheritDoc} + */ + @Override + public boolean moveUser(MoveUserCmd cmd) { + return _regionMgr.moveUser(cmd); + } + /** * {@inheritDoc} */ diff --git a/server/test/com/cloud/user/MockAccountManagerImpl.java b/server/test/com/cloud/user/MockAccountManagerImpl.java index 9429c86a117..8ea0473aa36 100644 --- a/server/test/com/cloud/user/MockAccountManagerImpl.java +++ b/server/test/com/cloud/user/MockAccountManagerImpl.java @@ -23,6 +23,7 @@ import java.net.InetAddress; import javax.naming.ConfigurationException; import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd; +import org.apache.cloudstack.api.command.admin.user.MoveUserCmd; import org.apache.cloudstack.framework.config.ConfigKey; import org.springframework.stereotype.Component; @@ -122,6 +123,10 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco return false; } + @Override public boolean moveUser(MoveUserCmd moveUserCmd) { + return false; + } + @Override public boolean isAdmin(Long accountId) { // TODO Auto-generated method stub diff --git a/test/integration/smoke/test_accounts.py b/test/integration/smoke/test_accounts.py index 00047bf5c2e..dffb00aaef4 100644 --- a/test/integration/smoke/test_accounts.py +++ b/test/integration/smoke/test_accounts.py @@ -2086,3 +2086,140 @@ class TestDomainForceRemove(cloudstackTestCase): domain.delete(self.apiclient, cleanup=False) return +class TestMoveUser(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.testClient = super(TestMoveUser, cls).getClsTestClient() + cls.api_client = cls.testClient.getApiClient() + cls.testdata = cls.testClient.getParsedTestDataConfig() + + cls.domain = get_domain(cls.api_client) + cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) + cls.testdata['mode'] = cls.zone.networktype + + cls.template = get_test_template( + cls.api_client, + cls.zone.id, + cls.testdata["ostype"] + ) + + cls.testdata["virtual_machine"]["zoneid"] = cls.zone.id + cls._cleanup = [] + return + + @classmethod + def tearDownClass(cls): + try: + # Clean up, terminate the created resources + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + self.testdata = self.testClient.getParsedTestDataConfig() + self.account1 = Account.create( + self.apiclient, + self.testdata["acl"]["accountD1"], + domainid=self.domain.id + ) + self.cleanup.append(self.account1) + + self.account2 = Account.create( + self.apiclient, + self.testdata["acl"]["accountD1A"], + domainid=self.domain.id + ) + self.cleanup.append(self.account2) + + self.user = User.create( + self.apiclient, + self.testdata["user"], + account=self.account1.name, + domainid=self.account1.domainid + ) + + return + + def tearDown(self): + try: + # Clean up, terminate the created resources + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["domains", "advanced", "advancedns", "simulator","dvs"], required_hardware="false") + def test_move_user_to_accountID(self): + + self.user.move(self.api_client, dest_accountid=self.account2.id) + + self.assertEqual( + self.account2.name, + self.user.list(self.apiclient, id=self.user.id)[0].account, + "Check user source of created user" + ) + return + + @attr(tags=["domains", "advanced", "advancedns", "simulator","dvs"], required_hardware="false") + def test_move_user_to_account_name(self): + + self.user.move(self.api_client, dest_account=self.account2.name) + + self.assertEqual( + self.account2.name, + self.user.list(self.apiclient, id=self.user.id)[0].account, + "Check user source of created user" + ) + return + + @attr(tags=["domains", "advanced", "advancedns", "simulator","dvs"], required_hardware="false") + def test_move_user_to_different_domain(self): + domain2 = Domain.create(self.api_client, + self.testdata["domain"], + parentdomainid=self.domain.id + ) + self.cleanup.append(domain2) + + account_different_domain = Account.create( + self.apiclient, + self.testdata["acl"]["accountD1B"], + domainid=domain2.id + ) + self.cleanup.append(account_different_domain) + try: + self.user.move(self.api_client, dest_account=account_different_domain.name) + except Exception: + pass + else: + self.fail("It should not be allowed to move users across accounts in different domains, failing") + + account_different_domain.delete(self.api_client) + return + + @attr(tags=["domains", "advanced", "advancedns", "simulator","dvs"], required_hardware="false") + def test_move_user_incorrect_account_id(self): + + try: + self.user.move(self.api_client, dest_accountid='incorrect-account-id') + except Exception: + pass + else: + self.fail("moving to non-existing account should not be possible, failing") + return + + @attr(tags=["domains", "advanced", "advancedns", "simulator","dvs"], required_hardware="false") + def test_move_user_incorrect_account_name(self): + + try: + self.user.move(self.api_client, dest_account='incorrect-account-name') + except Exception: + pass + else: + self.fail("moving to non-existing account should not be possible, failing") + return diff --git a/tools/marvin/marvin/config/test_data.py b/tools/marvin/marvin/config/test_data.py index 64733f7e179..7e7a2fdb7de 100644 --- a/tools/marvin/marvin/config/test_data.py +++ b/tools/marvin/marvin/config/test_data.py @@ -71,6 +71,15 @@ test_data = { "username": "test-account2", "password": "password" }, + "user": { + "email": "user@test.com", + "firstname": "User", + "lastname": "User", + "username": "User", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, "small": { "displayname": "testserver", "username": "root", diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index 4154e91cba2..f66a209bed7 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -267,6 +267,19 @@ class User: cmd.id = self.id apiclient.deleteUser(cmd) + def move(self, api_client, dest_accountid = None, dest_account = None, domain= None): + + if all([dest_account, dest_accountid]) is None: + raise Exception("Please add either destination account or destination account ID.") + + cmd = moveUser.moveUserCmd() + cmd.id = self.id + cmd.accountid = dest_accountid + cmd.account = dest_account + cmd.domain = domain + + return api_client.moveUser(cmd) + @classmethod def list(cls, apiclient, **kwargs): """Lists users and provides detailed account information for