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