This is an automated email from the ASF dual-hosted git repository. rafael pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/master by this push: new 3adc2b8 [CLOUDSTACK-5235] ask users current password when they are executing a password update (#2574) 3adc2b8 is described below commit 3adc2b8485d8a56634a3d4c54074321431bf2fda Author: Rafael Weingärtner <rafaelweingart...@gmail.com> AuthorDate: Wed May 2 09:19:06 2018 -0300 [CLOUDSTACK-5235] ask users current password when they are executing a password update (#2574) * [CLOUDSTACK-5235] Force users to enter old password when updating password * Formatting for checkstyle * Remove an unused import in AccountManagerImpl * Apply Nitin's suggestions * Change 'oldPassword' to 'currentPassword' * Second review of Resmo * Fix typos found by Nitin --- .../main/java/com/cloud/user/AccountService.java | 45 +- .../org/apache/cloudstack/api/ApiConstants.java | 4 +- .../api/command/admin/user/UpdateUserCmd.java | 30 +- .../contrail/management/MockAccountManager.java | 7 - .../cloudstack/api/command/LdapImportUsersCmd.java | 52 +- .../main/java/com/cloud/user/AccountManager.java | 90 +-- .../java/com/cloud/user/AccountManagerImpl.java | 444 ++++++++------ .../com/cloud/user/AccountManagerImplTest.java | 676 ++++++++++++++++++--- .../AccountManagerImplVolumeDeleteEventTest.java | 56 +- .../com/cloud/user/AccountManagetImplTestBase.java | 48 +- .../com/cloud/user/MockAccountManagerImpl.java | 7 - .../java/com/cloud/user/MockDomainManagerImpl.java | 164 ----- ui/l10n/ar.js | 1 + ui/l10n/ca.js | 1 + ui/l10n/de_DE.js | 1 + ui/l10n/en.js | 1 + ui/l10n/es.js | 1 + ui/l10n/fr_FR.js | 1 + ui/l10n/hu.js | 1 + ui/l10n/it_IT.js | 1 + ui/l10n/ja_JP.js | 1 + ui/l10n/ko_KR.js | 1 + ui/l10n/nb_NO.js | 1 + ui/l10n/nl_NL.js | 1 + ui/l10n/pl.js | 1 + ui/l10n/pt_BR.js | 1 + ui/l10n/ru_RU.js | 1 + ui/l10n/zh_CN.js | 1 + ui/scripts/accounts.js | 15 +- 29 files changed, 998 insertions(+), 656 deletions(-) diff --git a/api/src/main/java/com/cloud/user/AccountService.java b/api/src/main/java/com/cloud/user/AccountService.java index 9683d9f..060861d 100644 --- a/api/src/main/java/com/cloud/user/AccountService.java +++ b/api/src/main/java/com/cloud/user/AccountService.java @@ -23,53 +23,28 @@ import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd; import org.apache.cloudstack.api.command.admin.user.RegisterCmd; +import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; import com.cloud.domain.Domain; import com.cloud.exception.PermissionDeniedException; import com.cloud.offering.DiskOffering; import com.cloud.offering.ServiceOffering; - public interface AccountService { /** * Creates a new user and account, stores the password as is so encrypted passwords are recommended. - * - * @param userName - * TODO - * @param password - * TODO - * @param firstName - * TODO - * @param lastName - * TODO - * @param email - * TODO - * @param timezone - * TODO - * @param accountName - * TODO - * @param accountType - * TODO - * @param domainId - * TODO - * @param networkDomain - * TODO - * * @return the user if created successfully, null otherwise */ - UserAccount createUserAccount(String userName, String password, String firstName, String lastName, String email, String timezone, String accountName, - short accountType, Long roleId, Long domainId, String networkDomain, Map<String, String> details, String accountUUID, String userUUID); + UserAccount createUserAccount(String userName, String password, String firstName, String lastName, String email, String timezone, String accountName, short accountType, Long roleId, Long domainId, + String networkDomain, Map<String, String> details, String accountUUID, String userUUID); - UserAccount createUserAccount(String userName, String password, String firstName, String lastName, String email, String timezone, String accountName, short accountType, Long roleId, Long domainId, String networkDomain, - Map<String, String> details, String accountUUID, String userUUID, User.Source source); + UserAccount createUserAccount(String userName, String password, String firstName, String lastName, String email, String timezone, String accountName, short accountType, Long roleId, Long domainId, + String networkDomain, Map<String, String> details, String accountUUID, String userUUID, User.Source source); /** * Locks a user by userId. A locked user cannot access the API, but will still have running VMs/IP addresses * allocated/etc. - * - * @param userId - * @return UserAccount object */ UserAccount lockUser(long userId); @@ -79,8 +54,7 @@ public interface AccountService { User createUser(String userName, String password, String firstName, String lastName, String email, String timeZone, String accountName, Long domainId, String userUUID); - User createUser(String userName, String password, String firstName, String lastName, String email, String timeZone, String accountName, Long domainId, String userUUID, - User.Source source); + User createUser(String userName, String password, String firstName, String lastName, String email, String timeZone, String accountName, Long domainId, String userUUID, User.Source source); boolean isAdmin(Long accountId); @@ -90,7 +64,7 @@ public interface AccountService { UserAccount getActiveUserAccount(String username, Long domainId); - UserAccount updateUser(Long userId, String firstName, String lastName, String email, String userName, String password, String apiKey, String secretKey, String timeZone); + UserAccount updateUser(UpdateUserCmd updateUserCmd); Account getActiveAccountById(long accountId); @@ -128,15 +102,14 @@ public interface AccountService { void checkAccess(User user, ControlledEntity entity); - void checkAccess(Account account, AccessType accessType, boolean sameOwner, String apiName, - ControlledEntity... entities) throws PermissionDeniedException; + void checkAccess(Account account, AccessType accessType, boolean sameOwner, String apiName, ControlledEntity... entities) throws PermissionDeniedException; Long finalyzeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly); /** * returns the user account object for a given user id * @param userId user id - * @return useraccount object if it exists else null + * @return {@link UserAccount} object if it exists else null */ UserAccount getUserAccountById(Long userId); diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 5cd069b..8886320 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -215,8 +215,8 @@ public class ApiConstants { public static final String PARENT_DOMAIN_ID = "parentdomainid"; public static final String PARENT_TEMPLATE_ID = "parenttemplateid"; public static final String PASSWORD = "password"; + public static final String CURRENT_PASSWORD = "currentpassword"; public static final String SHOULD_UPDATE_PASSWORD = "update_passwd_on_host"; - public static final String NEW_PASSWORD = "new_password"; public static final String PASSWORD_ENABLED = "passwordenabled"; public static final String SSHKEY_ENABLED = "sshkeyenabled"; public static final String PATH = "path"; @@ -729,4 +729,4 @@ public class ApiConstants { public enum DomainDetails { all, resource, min; } -} +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java index 91ccede..24624e2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java @@ -34,7 +34,7 @@ import com.cloud.user.User; import com.cloud.user.UserAccount; @APICommand(name = "updateUser", description = "Updates a user account", responseObject = UserResponse.class, - requestHasSensitiveInfo = true, responseHasSensitiveInfo = true) +requestHasSensitiveInfo = true, responseHasSensitiveInfo = true) public class UpdateUserCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(UpdateUserCmd.class.getName()); @@ -65,20 +65,22 @@ public class UpdateUserCmd extends BaseCmd { acceptedOnAdminPort = false) private String password; + @Parameter(name = ApiConstants.CURRENT_PASSWORD, type = CommandType.STRING, description = "Current password that was being used by the user. You must inform the current password when updating the password.", acceptedOnAdminPort = false) + private String currentPassword; @Parameter(name = ApiConstants.SECRET_KEY, type = CommandType.STRING, description = "The secret key for the user. Must be specified with userApiKey") private String secretKey; @Parameter(name = ApiConstants.TIMEZONE, - type = CommandType.STRING, - description = "Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.") + type = CommandType.STRING, + description = "Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.") private String timezone; @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "Unique username") private String username; @Inject - RegionService _regionService; + private RegionService _regionService; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -108,6 +110,10 @@ public class UpdateUserCmd extends BaseCmd { return password; } + public String getCurrentPassword() { + return currentPassword; + } + public String getSecretKey() { return secretKey; } @@ -152,4 +158,20 @@ public class UpdateUserCmd extends BaseCmd { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update user"); } } + + public void setId(Long id) { + this.id = id; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public void setEmail(String email) { + this.email = email; + } } diff --git a/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java b/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java index 37ca2bc..100f380 100644 --- a/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java +++ b/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java @@ -168,13 +168,6 @@ public class MockAccountManager extends ManagerBase implements AccountManager { } @Override - public UserAccount updateUser(Long userId, String firstName, String lastName, String email, String userName, String password, String apiKey, String secretKey, - String timeZone) { - // TODO Auto-generated method stub - return null; - } - - @Override public User getActiveUser(long arg0) { return _systemUser; } diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapImportUsersCmd.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapImportUsersCmd.java index 564c1d0..9011452 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapImportUsersCmd.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapImportUsersCmd.java @@ -26,9 +26,6 @@ import java.util.UUID; import javax.inject.Inject; -import com.cloud.user.Account; -import com.cloud.user.User; -import com.cloud.user.UserAccount; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -36,6 +33,7 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.LdapUserResponse; import org.apache.cloudstack.api.response.ListResponse; @@ -54,25 +52,23 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; import com.cloud.user.AccountService; import com.cloud.user.DomainService; +import com.cloud.user.User; +import com.cloud.user.UserAccount; -@APICommand(name = "importLdapUsers", description = "Import LDAP users", responseObject = LdapUserResponse.class, since = "4.3.0", - requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +@APICommand(name = "importLdapUsers", description = "Import LDAP users", responseObject = LdapUserResponse.class, since = "4.3.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class LdapImportUsersCmd extends BaseListCmd { public static final Logger s_logger = Logger.getLogger(LdapImportUsersCmd.class.getName()); private static final String s_name = "ldapuserresponse"; - @Parameter(name = ApiConstants.TIMEZONE, - type = CommandType.STRING, - description = "Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.") + @Parameter(name = ApiConstants.TIMEZONE, type = CommandType.STRING, description = "Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.") private String timezone; - @Parameter(name = ApiConstants.ACCOUNT_TYPE, - type = CommandType.SHORT, - description = "Type of the account. Specify 0 for user, 1 for root admin, and 2 for domain admin") + @Parameter(name = ApiConstants.ACCOUNT_TYPE, type = CommandType.SHORT, description = "Type of the account. Specify 0 for user, 1 for root admin, and 2 for domain admin") private Short accountType; @Parameter(name = ApiConstants.ROLE_ID, type = CommandType.UUID, entityType = RoleResponse.class, description = "Creates the account under the specified role.") @@ -81,16 +77,13 @@ public class LdapImportUsersCmd extends BaseListCmd { @Parameter(name = ApiConstants.ACCOUNT_DETAILS, type = CommandType.MAP, description = "details for account used to store specific parameters") private Map<String, String> details; - @Parameter(name = ApiConstants.DOMAIN_ID, - type = CommandType.UUID, - entityType = DomainResponse.class, - description = "Specifies the domain to which the ldap users are to be " - + "imported. If no domain is specified, a domain will created using group parameter. If the group is also not specified, a domain name based on the OU information will be " - + "created. If no OU hierarchy exists, will be defaulted to ROOT domain") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Specifies the domain to which the ldap users are to be " + + "imported. If no domain is specified, a domain will created using group parameter. If the group is also not specified, a domain name based on the OU information will be " + + "created. If no OU hierarchy exists, will be defaulted to ROOT domain") private Long domainId; @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "Specifies the group name from which the ldap users are to be imported. " - + "If no group is specified, all the users will be imported.") + + "If no group is specified, all the users will be imported.") private String groupName; private Domain _domain; @@ -121,20 +114,27 @@ public class LdapImportUsersCmd extends BaseListCmd { } else { // check if the user exists. if yes, call update UserAccount csuser = _accountService.getActiveUserAccount(user.getUsername(), domain.getId()); - if(csuser == null) { + if (csuser == null) { s_logger.debug("No user exists with name: " + user.getUsername() + " creating a user in the account: " + accountName); _accountService.createUser(user.getUsername(), generatePassword(), user.getFirstname(), user.getLastname(), user.getEmail(), timezone, accountName, domain.getId(), - UUID.randomUUID().toString(), User.Source.LDAP); + UUID.randomUUID().toString(), User.Source.LDAP); } else { - s_logger.debug("account with name: " + accountName + " exist and user with name: " + user.getUsername() + " exists in the account. Updating the account."); - _accountService.updateUser(csuser.getId(), user.getFirstname(), user.getLastname(), user.getEmail(), null, null, null, null, null); + s_logger.debug("Account [name=%s] and user [name=%s] already exist in CloudStack. Executing the user update."); + + UpdateUserCmd updateUserCmd = new UpdateUserCmd(); + updateUserCmd.setId(csuser.getId()); + updateUserCmd.setFirstname(user.getFirstname()); + updateUserCmd.setLastname(user.getLastname()); + updateUserCmd.setEmail(user.getEmail()); + + _accountService.updateUser(updateUserCmd); } } } @Override - public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, - ResourceAllocationException, NetworkRuleConflictException { + public void execute() + throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { if (getAccountType() == null && getRoleId() == null) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Both account type and role ID are not provided"); } @@ -177,7 +177,7 @@ public class LdapImportUsersCmd extends BaseListCmd { private String getAccountName(LdapUser user) { String finalAccountName = accountName; - if(finalAccountName == null ) { + if (finalAccountName == null) { finalAccountName = user.getUsername(); } return finalAccountName; @@ -244,7 +244,7 @@ public class LdapImportUsersCmd extends BaseListCmd { final byte bytes[] = new byte[20]; randomGen.nextBytes(bytes); return new String(Base64.encode(bytes), "UTF-8"); - } catch ( NoSuchAlgorithmException | UnsupportedEncodingException e) { + } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to generate random password"); } } diff --git a/server/src/main/java/com/cloud/user/AccountManager.java b/server/src/main/java/com/cloud/user/AccountManager.java index e708b04..de6dcca 100644 --- a/server/src/main/java/com/cloud/user/AccountManager.java +++ b/server/src/main/java/com/cloud/user/AccountManager.java @@ -16,15 +16,17 @@ // under the License. package com.cloud.user; +import java.net.InetAddress; import java.util.List; import java.util.Map; -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 org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; import com.cloud.api.query.vo.ControlledViewEntity; import com.cloud.exception.ConcurrentOperationException; @@ -34,17 +36,14 @@ import com.cloud.utils.Pair; import com.cloud.utils.Ternary; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import org.apache.cloudstack.framework.config.ConfigKey; -import org.apache.cloudstack.framework.config.Configurable; /** * AccountManager includes logic that deals with accounts, domains, and users. * */ -public interface AccountManager extends AccountService, Configurable{ +public interface AccountManager extends AccountService, Configurable { /** * Disables an account by accountId - * @param accountId * @return true if disable was successful, false otherwise */ boolean disableAccount(long accountId) throws ConcurrentOperationException, ResourceUnavailableException; @@ -57,24 +56,23 @@ public interface AccountManager extends AccountService, Configurable{ /** * Logs out a user - * @param userId */ void logoutUser(long userId); /** - * Authenticates a user when s/he logs in. - * - * @param username - * required username for authentication - * @param password - * password to use for authentication, can be null for single sign-on case - * @param domainId - * id of domain where user with username resides - * @param requestParameters - * the request parameters of the login request, which should contain timestamp of when the request signature is - * made, and the signature itself in the single sign-on case - * @return a user object, null if the user failed to authenticate - */ + * Authenticates a user when s/he logs in. + * + * @param username + * required username for authentication + * @param password + * password to use for authentication, can be null for single sign-on case + * @param domainId + * id of domain where user with username resides + * @param requestParameters + * the request parameters of the login request, which should contain timestamp of when the request signature is + * made, and the signature itself in the single sign-on case + * @return a user object, null if the user failed to authenticate + */ UserAccount authenticateUser(String username, String password, Long domainId, InetAddress loginIpAddress, Map<String, Object[]> requestParameters); /** @@ -88,23 +86,20 @@ public interface AccountManager extends AccountService, Configurable{ boolean enableAccount(long accountId); + void buildACLSearchBuilder(SearchBuilder<? extends ControlledEntity> sb, Long domainId, boolean isRecursive, List<Long> permittedAccounts, + ListProjectResourcesCriteria listProjectResourcesCriteria); - void buildACLSearchBuilder(SearchBuilder<? extends ControlledEntity> sb, Long domainId, - boolean isRecursive, List<Long> permittedAccounts, ListProjectResourcesCriteria listProjectResourcesCriteria); - - void buildACLViewSearchBuilder(SearchBuilder<? extends ControlledViewEntity> sb, Long domainId, - boolean isRecursive, List<Long> permittedAccounts, ListProjectResourcesCriteria listProjectResourcesCriteria); - - void buildACLSearchCriteria(SearchCriteria<? extends ControlledEntity> sc, - Long domainId, boolean isRecursive, List<Long> permittedAccounts, ListProjectResourcesCriteria listProjectResourcesCriteria); + void buildACLViewSearchBuilder(SearchBuilder<? extends ControlledViewEntity> sb, Long domainId, boolean isRecursive, List<Long> permittedAccounts, + ListProjectResourcesCriteria listProjectResourcesCriteria); - void buildACLSearchParameters(Account caller, Long id, - String accountName, Long projectId, List<Long> permittedAccounts, Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject, boolean listAll, - boolean forProjectInvitation); + void buildACLSearchCriteria(SearchCriteria<? extends ControlledEntity> sc, Long domainId, boolean isRecursive, List<Long> permittedAccounts, + ListProjectResourcesCriteria listProjectResourcesCriteria); - void buildACLViewSearchCriteria(SearchCriteria<? extends ControlledViewEntity> sc, - Long domainId, boolean isRecursive, List<Long> permittedAccounts, ListProjectResourcesCriteria listProjectResourcesCriteria); + void buildACLSearchParameters(Account caller, Long id, String accountName, Long projectId, List<Long> permittedAccounts, + Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject, boolean listAll, boolean forProjectInvitation); + void buildACLViewSearchCriteria(SearchCriteria<? extends ControlledViewEntity> sc, Long domainId, boolean isRecursive, List<Long> permittedAccounts, + ListProjectResourcesCriteria listProjectResourcesCriteria); /** * Deletes a user by userId @@ -127,10 +122,6 @@ public interface AccountManager extends AccountService, Configurable{ /** * Disables an account by accountName and domainId - * - * @param accountName - * @param domainId - * @param accountId * @param disabled * account if success * @return true if disable was successful, false otherwise @@ -142,33 +133,21 @@ public interface AccountManager extends AccountService, Configurable{ * * @param accountName * - the enableAccount command defining the accountId to be deleted. - * @param domainId - * TODO - * @param accountId - * @return account object */ Account enableAccount(String accountName, Long domainId, Long accountId); /** * Deletes user by Id - * @param deleteUserCmd - * @return */ 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 cmd - * @return UserAccount object - */ + @Override UserAccount updateUser(UpdateUserCmd cmd); /** @@ -196,10 +175,6 @@ public interface AccountManager extends AccountService, Configurable{ * * @param accountName * - the LockAccount command defining the accountId to be locked. - * @param domainId - * TODO - * @param accountId - * @return account object */ Account lockAccount(String accountName, Long domainId, Long accountId); @@ -208,13 +183,8 @@ public interface AccountManager extends AccountService, Configurable{ public static final String MESSAGE_ADD_ACCOUNT_EVENT = "Message.AddAccount.Event"; public static final String MESSAGE_REMOVE_ACCOUNT_EVENT = "Message.RemoveAccount.Event"; - public static final ConfigKey<Boolean> UseSecretKeyInResponse = new ConfigKey<Boolean>( - "Advanced", - Boolean.class, - "use.secret.key.in.response", - "false", - "This parameter allows the users to enable or disable of showing secret key as a part of response for various APIs. By default it is set to false.", - true); + public static final ConfigKey<Boolean> UseSecretKeyInResponse = new ConfigKey<Boolean>("Advanced", Boolean.class, "use.secret.key.in.response", "false", + "This parameter allows the users to enable or disable of showing secret key as a part of response for various APIs. By default it is set to false.", true); boolean moveUser(long id, Long domainId, long accountId); } diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java index dc9fdc0..b44e280 100644 --- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java +++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java @@ -23,7 +23,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; @@ -38,10 +37,6 @@ import javax.crypto.spec.SecretKeySpec; import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.lang.StringUtils; -import org.apache.log4j.Logger; - import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.QuerySelector; import org.apache.cloudstack.acl.RoleType; @@ -55,6 +50,7 @@ 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.config.ApiServiceConfiguration; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.ConfigKey; @@ -64,6 +60,11 @@ import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.region.gslb.GlobalLoadBalancerRuleDao; import org.apache.cloudstack.utils.baremetal.BaremetalUtils; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; import com.cloud.api.ApiDBUtils; import com.cloud.api.query.vo.ControlledViewEntity; @@ -172,8 +173,6 @@ import com.cloud.vm.snapshot.VMSnapshot; import com.cloud.vm.snapshot.VMSnapshotManager; import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; -import org.apache.cloudstack.config.ApiServiceConfiguration; - public class AccountManagerImpl extends ManagerBase implements AccountManager, Manager { public static final Logger s_logger = Logger.getLogger(AccountManagerImpl.class); @@ -540,8 +539,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M Account account = ApiDBUtils.findAccountById(entity.getAccountId()); domainId = account != null ? account.getDomainId() : -1; } - if (entity.getAccountId() != -1 && domainId != -1 && !(entity instanceof VirtualMachineTemplate) - && !(entity instanceof Network && accessType != null && accessType == AccessType.UseEntry) && !(entity instanceof AffinityGroup)) { + if (entity.getAccountId() != -1 && domainId != -1 && !(entity instanceof VirtualMachineTemplate) && !(entity instanceof Network && accessType != null && accessType == AccessType.UseEntry) + && !(entity instanceof AffinityGroup)) { List<ControlledEntity> toBeChecked = domains.get(entity.getDomainId()); // for templates, we don't have to do cross domains check if (toBeChecked == null) { @@ -563,7 +562,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M if (!granted) { assert false : "How can all of the security checkers pass on checking this check: " + entity; - throw new PermissionDeniedException("There's no way to confirm " + caller + " has access to " + entity); + throw new PermissionDeniedException("There's no way to confirm " + caller + " has access to " + entity); } } @@ -590,26 +589,27 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M public Long checkAccessAndSpecifyAuthority(Account caller, Long zoneId) { // We just care for resource domain admin for now. He should be permitted to see only his zone. if (isResourceDomainAdmin(caller.getAccountId())) { - if (zoneId == null) + if (zoneId == null) { return getZoneIdForAccount(caller); - else if (zoneId.compareTo(getZoneIdForAccount(caller)) != 0) + } else if (zoneId.compareTo(getZoneIdForAccount(caller)) != 0) { throw new PermissionDeniedException("Caller " + caller + "is not allowed to access the zone " + zoneId); - else + } else { return zoneId; - } - - else + } + } else { return zoneId; + } } private Long getZoneIdForAccount(Account account) { // Currently just for resource domain admin List<DataCenterVO> dcList = _dcDao.findZonesByDomainId(account.getDomainId()); - if (dcList != null && dcList.size() != 0) + if (dcList != null && dcList.size() != 0) { return dcList.get(0).getId(); - else + } else { throw new CloudRuntimeException("Failed to find any private zone for Resource domain admin."); + } } @@ -1011,13 +1011,12 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Override @ActionEvents({@ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_CREATE, eventDescription = "creating Account"), - @ActionEvent(eventType = EventTypes.EVENT_USER_CREATE, eventDescription = "creating User")}) - public UserAccount createUserAccount(final String userName, final String password, final String firstName, final String lastName, final String email, final String timezone, - String accountName, final short accountType, final Long roleId, Long domainId, final String networkDomain, final Map<String, String> details, String accountUUID, - final String userUUID) { + @ActionEvent(eventType = EventTypes.EVENT_USER_CREATE, eventDescription = "creating User")}) + public UserAccount createUserAccount(final String userName, final String password, final String firstName, final String lastName, final String email, final String timezone, String accountName, + final short accountType, final Long roleId, Long domainId, final String networkDomain, final Map<String, String> details, String accountUUID, final String userUUID) { - return createUserAccount(userName, password, firstName, lastName, email, timezone, accountName, accountType, roleId, domainId, networkDomain, details, accountUUID, - userUUID, User.Source.UNKNOWN); + return createUserAccount(userName, password, firstName, lastName, email, timezone, accountName, accountType, roleId, domainId, networkDomain, details, accountUUID, userUUID, + User.Source.UNKNOWN); } // /////////////////////////////////////////////////// @@ -1027,10 +1026,10 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Override @DB @ActionEvents({@ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_CREATE, eventDescription = "creating Account"), - @ActionEvent(eventType = EventTypes.EVENT_USER_CREATE, eventDescription = "creating User")}) - public UserAccount createUserAccount(final String userName, final String password, final String firstName, final String lastName, final String email, final String timezone, - String accountName, final short accountType, final Long roleId, Long domainId, final String networkDomain, final Map<String, String> details, String accountUUID, - final String userUUID, final User.Source source) { + @ActionEvent(eventType = EventTypes.EVENT_USER_CREATE, eventDescription = "creating User")}) + public UserAccount createUserAccount(final String userName, final String password, final String firstName, final String lastName, final String email, final String timezone, String accountName, + final short accountType, final Long roleId, Long domainId, final String networkDomain, final Map<String, String> details, String accountUUID, final String userUUID, + final User.Source source) { if (accountName == null) { accountName = userName; @@ -1058,7 +1057,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } // Check permissions - checkAccess(CallContext.current().getCallingAccount(), domain); + checkAccess(getCurrentCallingAccount(), domain); if (!_userAccountDao.validateUsernameInDomain(userName, domainId)) { throw new InvalidParameterValueException("The user " + userName + " already exists in domain " + domainId); @@ -1132,7 +1131,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M throw new CloudRuntimeException("The user cannot be created as domain " + domain.getName() + " is being deleted"); } - checkAccess(CallContext.current().getCallingAccount(), domain); + checkAccess(getCurrentCallingAccount(), domain); Account account = _accountDao.findEnabledAccount(accountName, domainId); if (account == null || account.getType() == Account.ACCOUNT_TYPE_PROJECT) { @@ -1153,157 +1152,246 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Override @ActionEvent(eventType = EventTypes.EVENT_USER_CREATE, eventDescription = "creating User") - public UserVO createUser(String userName, String password, String firstName, String lastName, String email, String timeZone, String accountName, Long domainId, - String userUUID) { + public UserVO createUser(String userName, String password, String firstName, String lastName, String email, String timeZone, String accountName, Long domainId, String userUUID) { return createUser(userName, password, firstName, lastName, email, timeZone, accountName, domainId, userUUID, User.Source.UNKNOWN); } @Override - @ActionEvent(eventType = EventTypes.EVENT_USER_UPDATE, eventDescription = "updating User") - public UserAccount updateUser(Long userId, String firstName, String lastName, String email, String userName, String password, String apiKey, String secretKey, - String timeZone) { - // Input validation - UserVO user = _userDao.getUser(userId); + @ActionEvent(eventType = EventTypes.EVENT_USER_UPDATE, eventDescription = "Updating User") + public UserAccount updateUser(UpdateUserCmd updateUserCmd) { + UserVO user = retrieveAndValidateUser(updateUserCmd); + s_logger.debug("Updating user with Id: " + user.getUuid()); - if (user == null) { - throw new InvalidParameterValueException("unable to find user by id"); - } + validateAndUpdatApiAndSecretKeyIfNeeded(updateUserCmd, user); + Account account = retrieveAndValidateAccount(user); - if ((apiKey == null && secretKey != null) || (apiKey != null && secretKey == null)) { - throw new InvalidParameterValueException("Please provide an userApiKey/userSecretKey pair"); - } + validateAndUpdateFirstNameIfNeeded(updateUserCmd, user); + validateAndUpdateLastNameIfNeeded(updateUserCmd, user); + validateAndUpdateUsernameIfNeeded(updateUserCmd, user, account); - // If the account is an admin type, return an error. We do not allow this - Account account = _accountDao.findById(user.getAccountId()); - if (account == null) { - throw new InvalidParameterValueException("unable to find user account " + user.getAccountId()); + validateUserPasswordAndUpdateIfNeeded(updateUserCmd.getPassword(), user, updateUserCmd.getCurrentPassword()); + String email = updateUserCmd.getEmail(); + if (StringUtils.isNotBlank(email)) { + user.setEmail(email); } - - // don't allow updating project account - if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) { - throw new InvalidParameterValueException("unable to find user by id"); + String timezone = updateUserCmd.getTimezone(); + if (StringUtils.isNotBlank(timezone)) { + user.setTimezone(timezone); + } + _userDao.update(user.getId(), user); + return _userAccountDao.findById(user.getId()); + } + + /** + * Updates the password in the user POJO if needed. If no password is provided, then the password is not updated. + * The following validations are executed if 'password' is not null. Admins (root admins or domain admins) can execute password updates without entering the current password. + * <ul> + * <li> If 'password' is blank, we throw an {@link InvalidParameterValueException}; + * <li> If 'current password' is not provided and user is not an Admin, we throw an {@link InvalidParameterValueException}; + * <li> If a normal user is calling this method, we use {@link #validateCurrentPassword(UserVO, String)} to check if the provided old password matches the database one; + * </ul> + * + * If all checks pass, we encode the given password with the most preferable password mechanism given in {@link #_userPasswordEncoders}. + */ + protected void validateUserPasswordAndUpdateIfNeeded(String newPassword, UserVO user, String currentPassword) { + if (newPassword == null) { + s_logger.trace("No new password to update for user: " + user.getUuid()); + return; } - - // don't allow updating system account - if (account.getId() == Account.ACCOUNT_ID_SYSTEM) { - throw new PermissionDeniedException("user id : " + userId + " is system account, update is not allowed"); + if (StringUtils.isBlank(newPassword)) { + throw new InvalidParameterValueException("Password cannot be empty or blank."); + } + Account callingAccount = getCurrentCallingAccount(); + boolean isRootAdminExecutingPasswordUpdate = callingAccount.getId() == Account.ACCOUNT_ID_SYSTEM || isRootAdmin(callingAccount.getId()); + boolean isDomainAdmin = isDomainAdmin(callingAccount.getId()); + boolean isAdmin = isDomainAdmin || isRootAdminExecutingPasswordUpdate; + if (isAdmin) { + s_logger.trace(String.format("Admin account [uuid=%s] executing password update for user [%s] ", callingAccount.getUuid(), user.getUuid())); + } + if (!isAdmin && StringUtils.isBlank(currentPassword)) { + throw new InvalidParameterValueException("To set a new password the current password must be provided."); + } + if (CollectionUtils.isEmpty(_userPasswordEncoders)) { + throw new CloudRuntimeException("No user authenticators configured!"); + } + if (!isAdmin) { + validateCurrentPassword(user, currentPassword); + } + UserAuthenticator userAuthenticator = _userPasswordEncoders.get(0); + String newPasswordEncoded = userAuthenticator.encode(newPassword); + user.setPassword(newPasswordEncoded); + } + + /** + * Iterates over all configured user authenticators and tries to authenticate the user using the current password. + * If the user is authenticated with success, we have nothing else to do here; otherwise, an {@link InvalidParameterValueException} is thrown. + */ + protected void validateCurrentPassword(UserVO user, String currentPassword) { + AccountVO userAccount = _accountDao.findById(user.getAccountId()); + boolean currentPasswordMatchesDataBasePassword = false; + for (UserAuthenticator userAuthenticator : _userPasswordEncoders) { + Pair<Boolean, ActionOnFailedAuthentication> authenticationResult = userAuthenticator.authenticate(user.getUsername(), currentPassword, userAccount.getDomainId(), null); + if (authenticationResult == null) { + s_logger.trace(String.format("Authenticator [%s] is returning null for the authenticate mehtod.", userAuthenticator.getClass())); + continue; + } + if (BooleanUtils.toBoolean(authenticationResult.first())) { + s_logger.debug(String.format("User [id=%s] re-authenticated [authenticator=%s] during password update.", user.getUuid(), userAuthenticator.getName())); + currentPasswordMatchesDataBasePassword = true; + break; + } } + if (!currentPasswordMatchesDataBasePassword) { + throw new InvalidParameterValueException("Current password is incorrect."); + } + } - checkAccess(CallContext.current().getCallingAccount(), AccessType.OperateEntry, true, account); - - if (firstName != null) { - if (firstName.isEmpty()) { - throw new InvalidParameterValueException("Firstname is empty"); + /** + * Validates the user 'username' if provided. The 'username' cannot be blank (when provided). + * <ul> + * <li> If the 'username' is not provided, we do not update it (setting to null) in the User POJO. + * <li> If the 'username' is blank, we throw an {@link InvalidParameterValueException}. + * <li> The username must be unique in each domain. Therefore, if there is already another user with the same username, an {@link InvalidParameterValueException} is thrown. + * </ul> + */ + protected void validateAndUpdateUsernameIfNeeded(UpdateUserCmd updateUserCmd, UserVO user, Account account) { + String userName = updateUserCmd.getUsername(); + if (userName == null) { + return; + } + if (StringUtils.isBlank(userName)) { + throw new InvalidParameterValueException("Username cannot be empty."); + } + List<UserVO> duplicatedUsers = _userDao.findUsersByName(userName); + for (UserVO duplicatedUser : duplicatedUsers) { + if (duplicatedUser.getId() == user.getId()) { + continue; + } + Account duplicatedUserAccountWithUserThatHasTheSameUserName = _accountDao.findById(duplicatedUser.getAccountId()); + if (duplicatedUserAccountWithUserThatHasTheSameUserName.getDomainId() == account.getDomainId()) { + DomainVO domain = _domainDao.findById(duplicatedUserAccountWithUserThatHasTheSameUserName.getDomainId()); + throw new InvalidParameterValueException(String.format("Username [%s] already exists in domain [id=%s,name=%s]", duplicatedUser.getUsername(), domain.getUuid(), domain.getName())); } - - user.setFirstname(firstName); } + user.setUsername(userName); + } + + /** + * Validates the user 'lastName' if provided. The 'lastName' cannot be blank (when provided). + * <ul> + * <li> If the 'lastName' is not provided, we do not update it (setting to null) in the User POJO. + * <li> If the 'lastName' is blank, we throw an {@link InvalidParameterValueException}. + * </ul> + */ + protected void validateAndUpdateLastNameIfNeeded(UpdateUserCmd updateUserCmd, UserVO user) { + String lastName = updateUserCmd.getLastname(); if (lastName != null) { - if (lastName.isEmpty()) { - throw new InvalidParameterValueException("Lastname is empty"); + if (StringUtils.isBlank(lastName)) { + throw new InvalidParameterValueException("Lastname cannot be empty."); } user.setLastname(lastName); } - if (userName != null) { - if (userName.isEmpty()) { - throw new InvalidParameterValueException("Username is empty"); - } + } - // don't allow to have same user names in the same domain - List<UserVO> duplicatedUsers = _userDao.findUsersByName(userName); - for (UserVO duplicatedUser : duplicatedUsers) { - if (duplicatedUser.getId() != user.getId()) { - Account duplicatedUserAccount = _accountDao.findById(duplicatedUser.getAccountId()); - if (duplicatedUserAccount.getDomainId() == account.getDomainId()) { - throw new InvalidParameterValueException("User with name " + userName + " already exists in domain " + duplicatedUserAccount.getDomainId()); - } - } + /** + * Validates the user 'firstName' if provided. The 'firstName' cannot be blank (when provided). + * <ul> + * <li> If the 'firstName' is not provided, we do not update it (setting to null) in the User POJO. + * <li> If the 'firstName' is blank, we throw an {@link InvalidParameterValueException}. + * </ul> + */ + protected void validateAndUpdateFirstNameIfNeeded(UpdateUserCmd updateUserCmd, UserVO user) { + String firstName = updateUserCmd.getFirstname(); + if (firstName != null) { + if (StringUtils.isBlank(firstName)) { + throw new InvalidParameterValueException("Firstname cannot be empty."); } - - user.setUsername(userName); + user.setFirstname(firstName); } + } - if (password != null) { - if (password.isEmpty()) { - throw new InvalidParameterValueException("Password cannot be empty"); - } - String encodedPassword = null; - for (Iterator<UserAuthenticator> en = _userPasswordEncoders.iterator(); en.hasNext();) { - UserAuthenticator authenticator = en.next(); - encodedPassword = authenticator.encode(password); - if (encodedPassword != null) { - break; - } - } - if (encodedPassword == null) { - throw new CloudRuntimeException("Failed to encode password"); - } - user.setPassword(encodedPassword); - } - if (email != null) { - user.setEmail(email); - } - if (timeZone != null) { - user.setTimezone(timeZone); + /** + * Searches an account for the given users. Then, we validate it as follows: + * <ul> + * <li>If no account is found for the given user, we throw a {@link CloudRuntimeException}. There must be something wrong in the database for this case. + * <li>If the account is of {@link Account#ACCOUNT_TYPE_PROJECT}, we throw an {@link InvalidParameterValueException}. + * <li>If the account is of {@link Account#ACCOUNT_ID_SYSTEM}, we throw an {@link InvalidParameterValueException}. + * </ul> + * + * Afterwards, we check if the logged user has access to the user being updated via {@link #checkAccess(Account, AccessType, boolean, ControlledEntity...)} + */ + protected Account retrieveAndValidateAccount(UserVO user) { + Account account = _accountDao.findById(user.getAccountId()); + if (account == null) { + throw new CloudRuntimeException("Unable to find user account with ID: " + user.getAccountId()); } - if (apiKey != null) { - user.setApiKey(apiKey); + if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) { + throw new InvalidParameterValueException("Unable to find user with ID: " + user.getUuid()); } - if (secretKey != null) { - user.setSecretKey(secretKey); + if (account.getId() == Account.ACCOUNT_ID_SYSTEM) { + throw new PermissionDeniedException("user UUID : " + user.getUuid() + " is a system account; update is not allowed."); } + checkAccess(getCurrentCallingAccount(), AccessType.OperateEntry, true, account); + return account; + } - if (s_logger.isDebugEnabled()) { - s_logger.debug("updating user with id: " + userId); + /** + * Returns the calling account using the method {@link CallContext#getCallingAccount()}. + * We are introducing this method to avoid using 'PowerMockRunner' in unit tests. Then, we can mock the calls to this method, which facilitates the development of test cases. + */ + protected Account getCurrentCallingAccount() { + return CallContext.current().getCallingAccount(); + } + + /** + * Validates user API and Secret keys. If a new pair of keys is provided, we update them in the user POJO. + * <ul> + * <li>When updating the keys, it must be provided a pair (API and Secret keys); otherwise, an {@link InvalidParameterValueException} is thrown. + * <li>If a pair of keys is provided, we validate to see if there is an user already using the provided API key. If there is someone else using, we throw an {@link InvalidParameterValueException} because two users cannot have the same API key. + * </ul> + */ + protected void validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmd updateUserCmd, UserVO user) { + String apiKey = updateUserCmd.getApiKey(); + String secretKey = updateUserCmd.getSecretKey(); + + boolean isApiKeyBlank = StringUtils.isBlank(apiKey); + boolean isSecretKeyBlank = StringUtils.isBlank(secretKey); + if (isApiKeyBlank ^ isSecretKeyBlank) { + throw new InvalidParameterValueException("Please provide a userApiKey/userSecretKey pair"); } - try { - // check if the apiKey and secretKey are globally unique - if (apiKey != null && secretKey != null) { - Pair<User, Account> apiKeyOwner = _accountDao.findUserAccountByApiKey(apiKey); - - if (apiKeyOwner != null) { - User usr = apiKeyOwner.first(); - if (usr.getId() != userId) { - throw new InvalidParameterValueException("The api key:" + apiKey + " exists in the system for user id:" + userId + " ,please provide a unique key"); - } else { - // allow the updation to take place - } - } + if (isApiKeyBlank && isSecretKeyBlank) { + return; + } + Pair<User, Account> apiKeyOwner = _accountDao.findUserAccountByApiKey(apiKey); + if (apiKeyOwner != null) { + User userThatHasTheProvidedApiKey = apiKeyOwner.first(); + if (userThatHasTheProvidedApiKey.getId() != user.getId()) { + throw new InvalidParameterValueException(String.format("The API key [%s] already exists in the system. Please provide a unique key.", apiKey)); } - - _userDao.update(userId, user); - } catch (Throwable th) { - s_logger.error("error updating user", th); - throw new CloudRuntimeException("Unable to update user " + userId); } - - CallContext.current().putContextParameter(User.class, user.getUuid()); - - return _userAccountDao.findById(userId); + user.setApiKey(apiKey); + user.setSecretKey(secretKey); } - @Override - @ActionEvent(eventType = EventTypes.EVENT_USER_UPDATE, eventDescription = "updating User") - public UserAccount updateUser(UpdateUserCmd cmd) { - Long id = cmd.getId(); - String apiKey = cmd.getApiKey(); - String firstName = cmd.getFirstname(); - String email = cmd.getEmail(); - String lastName = cmd.getLastname(); - String password = cmd.getPassword(); - String secretKey = cmd.getSecretKey(); - String timeZone = cmd.getTimezone(); - String userName = cmd.getUsername(); + /** + * Searches for a user with the given userId. If no user is found we throw an {@link InvalidParameterValueException}. + */ + protected UserVO retrieveAndValidateUser(UpdateUserCmd updateUserCmd) { + Long userId = updateUserCmd.getId(); - return updateUser(id, firstName, lastName, email, userName, password, apiKey, secretKey, timeZone); + UserVO user = _userDao.getUser(userId); + if (user == null) { + throw new InvalidParameterValueException("Unable to find user with id: " + userId); + } + return user; } @Override @ActionEvent(eventType = EventTypes.EVENT_USER_DISABLE, eventDescription = "disabling User", async = true) public UserAccount disableUser(long userId) { - Account caller = CallContext.current().getCallingAccount(); + Account caller = getCurrentCallingAccount(); // Check if user exists in the system User user = _userDao.findById(userId); @@ -1345,7 +1433,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @ActionEvent(eventType = EventTypes.EVENT_USER_ENABLE, eventDescription = "enabling User") public UserAccount enableUser(final long userId) { - Account caller = CallContext.current().getCallingAccount(); + Account caller = getCurrentCallingAccount(); // Check if user exists in the system final User user = _userDao.findById(userId); @@ -1396,7 +1484,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Override @ActionEvent(eventType = EventTypes.EVENT_USER_LOCK, eventDescription = "locking User") public UserAccount lockUser(long userId) { - Account caller = CallContext.current().getCallingAccount(); + Account caller = getCurrentCallingAccount(); // Check if user with id exists in the system User user = _userDao.findById(userId); @@ -1462,7 +1550,6 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Override @ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_DELETE, eventDescription = "deleting account", async = true) - // This method deletes the account public boolean deleteUserAccount(long accountId) { CallContext ctx = CallContext.current(); @@ -1528,7 +1615,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } // Check if user performing the action is allowed to modify this account - Account caller = CallContext.current().getCallingAccount(); + Account caller = getCurrentCallingAccount(); checkAccess(caller, AccessType.OperateEntry, true, account); boolean success = enableAccount(account.getId()); @@ -1545,7 +1632,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Override @ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_DISABLE, eventDescription = "locking account", async = true) public AccountVO lockAccount(String accountName, Long domainId, Long accountId) { - Account caller = CallContext.current().getCallingAccount(); + Account caller = getCurrentCallingAccount(); Account account = null; if (accountId != null) { @@ -1575,7 +1662,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Override @ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_DISABLE, eventDescription = "disabling account", async = true) public AccountVO disableAccount(String accountName, Long domainId, Long accountId) throws ConcurrentOperationException, ResourceUnavailableException { - Account caller = CallContext.current().getCallingAccount(); + Account caller = getCurrentCallingAccount(); Account account = null; if (accountId != null) { @@ -1633,16 +1720,11 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } // Check if user performing the action is allowed to modify this account - checkAccess(CallContext.current().getCallingAccount(), _domainMgr.getDomain(account.getDomainId())); + checkAccess(getCurrentCallingAccount(), _domainMgr.getDomain(account.getDomainId())); // check if the given account name is unique in this domain for updating Account duplicateAcccount = _accountDao.findActiveAccount(newAccountName, domainId); - if (duplicateAcccount != null && duplicateAcccount.getId() != account.getId()) {// allow - // same - // account - // to - // update - // itself + if (duplicateAcccount != null && duplicateAcccount.getId() != account.getId()) { throw new InvalidParameterValueException( "There already exists an account with the name:" + newAccountName + " in the domain:" + domainId + " with existing account id:" + duplicateAcccount.getId()); } @@ -1700,6 +1782,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M return _userDao.remove(deleteUserCmd.getId()); } + @Override @ActionEvent(eventType = EventTypes.EVENT_USER_MOVE, eventDescription = "moving User to a new account") public boolean moveUser(MoveUserCmd cmd) { final Long id = cmd.getId(); @@ -1713,17 +1796,18 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M return moveUser(user, newAccountId); } + @Override public boolean moveUser(long id, Long domainId, long accountId) { UserVO user = getValidUserVO(id); Account oldAccount = _accountDao.findById(user.getAccountId()); checkAccountAndAccess(user, oldAccount); Account newAccount = _accountDao.findById(accountId); checkIfNotMovingAcrossDomains(domainId, newAccount); - return moveUser(user , accountId); + return moveUser(user, accountId); } private boolean moveUser(UserVO user, long newAccountId) { - if(newAccountId == user.getAccountId()) { + 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 } @@ -1734,7 +1818,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M UserVO newUser = new UserVO(user); user.setExternalEntity(user.getUuid()); user.setUuid(UUID.randomUUID().toString()); - _userDao.update(user.getId(),user); + _userDao.update(user.getId(), user); newUser.setAccountId(newAccountId); boolean success = _userDao.remove(user.getId()); UserVO persisted = _userDao.persist(newUser); @@ -1746,7 +1830,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M private long getNewAccountId(long domainId, String accountName, Long accountId) { Account newAccount = null; if (StringUtils.isNotBlank(accountName)) { - if(s_logger.isDebugEnabled()) { + if (s_logger.isDebugEnabled()) { s_logger.debug("Getting id for account by name '" + accountName + "' in domain " + domainId); } newAccount = _accountDao.findEnabledAccount(accountName, domainId); @@ -1763,7 +1847,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } private void checkIfNotMovingAcrossDomains(long domainId, Account newAccount) { - if(newAccount.getDomainId() != domainId) { + 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!"); } @@ -1775,7 +1859,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M throw new InvalidParameterValueException("Project users cannot be deleted or moved."); } - checkAccess(CallContext.current().getCallingAccount(), AccessType.OperateEntry, true, account); + checkAccess(getCurrentCallingAccount(), AccessType.OperateEntry, true, account); CallContext.current().putContextParameter(User.class, user.getUuid()); } @@ -1995,8 +2079,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Override @DB - public AccountVO createAccount(final String accountName, final short accountType, final Long roleId, final Long domainId, final String networkDomain, - final Map<String, String> details, final String uuid) { + public AccountVO createAccount(final String accountName, final short accountType, final Long roleId, final Long domainId, final String networkDomain, final Map<String, String> details, + final String uuid) { // Validate domain Domain domain = _domainMgr.getDomain(domainId); if (domain == null) { @@ -2064,8 +2148,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M }); } - protected UserVO createUser(long accountId, String userName, String password, String firstName, String lastName, String email, String timezone, String userUUID, - User.Source source) { + protected UserVO createUser(long accountId, String userName, String password, String firstName, String lastName, String email, String timezone, String userUUID, User.Source source) { if (s_logger.isDebugEnabled()) { s_logger.debug("Creating user: " + userName + ", accountId: " + accountId + " timezone:" + timezone); } @@ -2098,8 +2181,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } @Override - public UserAccount authenticateUser(final String username, final String password, final Long domainId, final InetAddress loginIpAddress, final Map<String, Object[]> - requestParameters) { + public UserAccount authenticateUser(final String username, final String password, final Long domainId, final InetAddress loginIpAddress, final Map<String, Object[]> requestParameters) { UserAccount user = null; if (password != null && !password.isEmpty()) { user = getUserAccount(username, password, domainId, requestParameters); @@ -2211,10 +2293,10 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M // We authenticated successfully by now, let's check if we are allowed to login from the ip address the reqest comes from final Account account = _accountMgr.getAccount(user.getAccountId()); - final DomainVO domain = (DomainVO) _domainMgr.getDomain(account.getDomainId()); + final DomainVO domain = (DomainVO)_domainMgr.getDomain(account.getDomainId()); // Get the CIDRs from where this account is allowed to make calls - final String accessAllowedCidrs = ApiServiceConfiguration.ApiAllowedSourceCidrList.valueIn(account.getId()).replaceAll("\\s",""); + final String accessAllowedCidrs = ApiServiceConfiguration.ApiAllowedSourceCidrList.valueIn(account.getId()).replaceAll("\\s", ""); final Boolean ApiSourceCidrChecksEnabled = ApiServiceConfiguration.ApiSourceCidrChecksEnabled.value(); if (ApiSourceCidrChecksEnabled) { @@ -2222,10 +2304,9 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M // Block when is not in the list of allowed IPs if (!NetUtils.isIpInCidrList(loginIpAddress, accessAllowedCidrs.split(","))) { - s_logger.warn("Request by account '" + account.toString() + "' was denied since " + loginIpAddress.toString().replaceAll("/","") - + " does not match " + accessAllowedCidrs); + s_logger.warn("Request by account '" + account.toString() + "' was denied since " + loginIpAddress.toString().replaceAll("/", "") + " does not match " + accessAllowedCidrs); throw new CloudAuthenticationException("Failed to authenticate user '" + username + "' in domain '" + domain.getPath() + "' from ip " - + loginIpAddress.toString().replaceAll("/","") + "; please provide valid credentials"); + + loginIpAddress.toString().replaceAll("/", "") + "; please provide valid credentials"); } } @@ -2234,8 +2315,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M s_logger.debug("User: " + username + " in domain " + domainId + " has successfully logged in"); } - ActionEventUtils.onActionEvent(user.getId(), user.getAccountId(), user.getDomainId(), EventTypes.EVENT_USER_LOGIN, - "user has logged in from IP Address " + loginIpAddress); + ActionEventUtils.onActionEvent(user.getId(), user.getAccountId(), user.getDomainId(), EventTypes.EVENT_USER_LOGIN, "user has logged in from IP Address " + loginIpAddress); return user; } else { @@ -2285,12 +2365,12 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M if (s_logger.isInfoEnabled()) { s_logger.info("User " + username + " in domain " + domainName + " is disabled/locked (or account is disabled/locked)"); } - throw new CloudAuthenticationException( - "User " + username + " (or their account) in domain " + domainName + " is disabled/locked. Please contact the administrator."); + throw new CloudAuthenticationException("User " + username + " (or their account) in domain " + domainName + " is disabled/locked. Please contact the administrator."); } // Whenever the user is able to log in successfully, reset the login attempts to zero - if (!isInternalAccount(userAccount.getId())) + if (!isInternalAccount(userAccount.getId())) { updateLoginAttempts(userAccount.getId(), 0, false); + } return userAccount; } else { @@ -2351,7 +2431,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @DB @ActionEvent(eventType = EventTypes.EVENT_REGISTER_FOR_SECRET_API_KEY, eventDescription = "register for the developer API keys") public String[] createApiKeyAndSecretKey(RegisterCmd cmd) { - Account caller = CallContext.current().getCallingAccount(); + Account caller = getCurrentCallingAccount(); final Long userId = cmd.getId(); User user = getUserIncludingRemoved(userId); @@ -2668,8 +2748,9 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Override public List<String> listAclGroupsByAccount(Long accountId) { - if (_querySelectors == null || _querySelectors.size() == 0) + if (_querySelectors == null || _querySelectors.size() == 0) { return new ArrayList<String>(); + } QuerySelector qs = _querySelectors.get(0); return qs.listAclGroupsByAccount(accountId); @@ -2692,8 +2773,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M if (!enabledOnly || account.getState() == Account.State.enabled) { return account.getId(); } else { - throw new PermissionDeniedException( - "Can't add resources to the account id=" + account.getId() + " in state=" + account.getState() + " as it's no longer active"); + throw new PermissionDeniedException("Can't add resources to the account id=" + account.getId() + " in state=" + account.getState() + " as it's no longer active"); } } else { // idList is not used anywhere, so removed it now diff --git a/server/src/test/java/com/cloud/user/AccountManagerImplTest.java b/server/src/test/java/com/cloud/user/AccountManagerImplTest.java index 9190cf8..fcb0c57 100644 --- a/server/src/test/java/com/cloud/user/AccountManagerImplTest.java +++ b/server/src/test/java/com/cloud/user/AccountManagerImplTest.java @@ -22,67 +22,100 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import com.cloud.acl.DomainChecker; -import com.cloud.exception.PermissionDeniedException; -import com.cloud.server.auth.UserAuthenticator; -import com.cloud.utils.Pair; - +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd; +import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; import org.apache.cloudstack.context.CallContext; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mockito; -import org.apache.cloudstack.acl.ControlledEntity; -import org.apache.cloudstack.acl.SecurityChecker.AccessType; -import com.cloud.vm.snapshot.VMSnapshotVO; +import org.mockito.runners.MockitoJUnitRunner; + +import com.cloud.acl.DomainChecker; import com.cloud.domain.Domain; import com.cloud.domain.DomainVO; import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceUnavailableException; +import com.cloud.server.auth.UserAuthenticator; +import com.cloud.server.auth.UserAuthenticator.ActionOnFailedAuthentication; import com.cloud.user.Account.State; +import com.cloud.utils.Pair; +import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.UserVmManagerImpl; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.snapshot.VMSnapshotVO; - +@RunWith(MockitoJUnitRunner.class) public class AccountManagerImplTest extends AccountManagetImplTestBase { + @Mock + private UserVmManagerImpl _vmMgr; + @Mock + private AccountVO callingAccount; + @Mock + private DomainChecker domainChecker; + @Mock + private AccountService accountService; + @Mock + private GetUserKeysCmd _listkeyscmd; + @Mock + private User _user; + @Mock + private UserAccountVO userAccountVO; + + @Mock + private UpdateUserCmd UpdateUserCmdMock; + + private long userVoIdMock = 111l; + @Mock + private UserVO userVoMock; + private long accountMockId = 100l; @Mock - UserVmManagerImpl _vmMgr; + private Account accountMock; + + @Before + public void beforeTest() { + Mockito.doReturn(accountMockId).when(accountMock).getId(); + Mockito.doReturn(accountMock).when(accountManagerImpl).getCurrentCallingAccount(); + + Mockito.doReturn(accountMockId).when(userVoMock).getAccountId(); + + Mockito.doReturn(userVoIdMock).when(userVoMock).getId(); + } @Test - public void disableAccountNotexisting() - throws ConcurrentOperationException, ResourceUnavailableException { - Mockito.when(_accountDao.findById(42l)).thenReturn(null); - Assert.assertTrue(accountManager.disableAccount(42)); + public void disableAccountNotexisting() throws ConcurrentOperationException, ResourceUnavailableException { + Mockito.when(accountDaoMock.findById(42l)).thenReturn(null); + Assert.assertTrue(accountManagerImpl.disableAccount(42)); } @Test - public void disableAccountDisabled() throws ConcurrentOperationException, - ResourceUnavailableException { + public void disableAccountDisabled() throws ConcurrentOperationException, ResourceUnavailableException { AccountVO disabledAccount = new AccountVO(); disabledAccount.setState(State.disabled); - Mockito.when(_accountDao.findById(42l)).thenReturn(disabledAccount); - Assert.assertTrue(accountManager.disableAccount(42)); + Mockito.when(accountDaoMock.findById(42l)).thenReturn(disabledAccount); + Assert.assertTrue(accountManagerImpl.disableAccount(42)); } @Test - public void disableAccount() throws ConcurrentOperationException, - ResourceUnavailableException { + public void disableAccount() throws ConcurrentOperationException, ResourceUnavailableException { AccountVO account = new AccountVO(); account.setState(State.enabled); - Mockito.when(_accountDao.findById(42l)).thenReturn(account); - Mockito.when(_accountDao.createForUpdate()).thenReturn(new AccountVO()); - Mockito.when( - _accountDao.update(Mockito.eq(42l), - Mockito.any(AccountVO.class))).thenReturn(true); - Mockito.when(_vmDao.listByAccountId(42l)).thenReturn( - Arrays.asList(Mockito.mock(VMInstanceVO.class))); - Assert.assertTrue(accountManager.disableAccount(42)); - Mockito.verify(_accountDao, Mockito.atLeastOnce()).update( - Mockito.eq(42l), Mockito.any(AccountVO.class)); + Mockito.when(accountDaoMock.findById(42l)).thenReturn(account); + Mockito.when(accountDaoMock.createForUpdate()).thenReturn(new AccountVO()); + Mockito.when(accountDaoMock.update(Mockito.eq(42l), Mockito.any(AccountVO.class))).thenReturn(true); + Mockito.when(_vmDao.listByAccountId(42l)).thenReturn(Arrays.asList(Mockito.mock(VMInstanceVO.class))); + Assert.assertTrue(accountManagerImpl.disableAccount(42)); + Mockito.verify(accountDaoMock, Mockito.atLeastOnce()).update(Mockito.eq(42l), Mockito.any(AccountVO.class)); } @Test @@ -90,20 +123,12 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase { AccountVO account = new AccountVO(); account.setId(42l); DomainVO domain = new DomainVO(); - Mockito.when(_accountDao.findById(42l)).thenReturn(account); - Mockito.when( - securityChecker.checkAccess(Mockito.any(Account.class), - Mockito.any(ControlledEntity.class), Mockito.any(AccessType.class), - Mockito.anyString())) - .thenReturn(true); - Mockito.when(_accountDao.remove(42l)).thenReturn(true); - Mockito.when(_configMgr.releaseAccountSpecificVirtualRanges(42l)) - .thenReturn(true); + Mockito.when(accountDaoMock.findById(42l)).thenReturn(account); + Mockito.when(securityChecker.checkAccess(Mockito.any(Account.class), Mockito.any(ControlledEntity.class), Mockito.any(AccessType.class), Mockito.anyString())).thenReturn(true); + Mockito.when(accountDaoMock.remove(42l)).thenReturn(true); + Mockito.when(_configMgr.releaseAccountSpecificVirtualRanges(42l)).thenReturn(true); Mockito.when(_domainMgr.getDomain(Mockito.anyLong())).thenReturn(domain); - Mockito.when( - securityChecker.checkAccess(Mockito.any(Account.class), - Mockito.any(Domain.class))) - .thenReturn(true); + Mockito.when(securityChecker.checkAccess(Mockito.any(Account.class), Mockito.any(Domain.class))).thenReturn(true); Mockito.when(_vmSnapshotDao.listByAccountId(Mockito.anyLong())).thenReturn(new ArrayList<VMSnapshotVO>()); List<SSHKeyPairVO> sshkeyList = new ArrayList<SSHKeyPairVO>(); @@ -113,10 +138,9 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase { Mockito.when(_sshKeyPairDao.listKeyPairs(Mockito.anyLong(), Mockito.anyLong())).thenReturn(sshkeyList); Mockito.when(_sshKeyPairDao.remove(Mockito.anyLong())).thenReturn(true); - Assert.assertTrue(accountManager.deleteUserAccount(42)); + Assert.assertTrue(accountManagerImpl.deleteUserAccount(42)); // assert that this was a clean delete - Mockito.verify(_accountDao, Mockito.never()).markForCleanup( - Mockito.eq(42l)); + Mockito.verify(accountDaoMock, Mockito.never()).markForCleanup(Mockito.eq(42l)); } @Test @@ -124,33 +148,20 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase { AccountVO account = new AccountVO(); account.setId(42l); DomainVO domain = new DomainVO(); - Mockito.when(_accountDao.findById(42l)).thenReturn(account); - Mockito.when( - securityChecker.checkAccess(Mockito.any(Account.class), - Mockito.any(ControlledEntity.class), Mockito.any(AccessType.class), - Mockito.anyString())) - .thenReturn(true); - Mockito.when(_accountDao.remove(42l)).thenReturn(true); - Mockito.when(_configMgr.releaseAccountSpecificVirtualRanges(42l)) - .thenReturn(true); - Mockito.when(_userVmDao.listByAccountId(42l)).thenReturn( - Arrays.asList(Mockito.mock(UserVmVO.class))); - Mockito.when( - _vmMgr.expunge(Mockito.any(UserVmVO.class), Mockito.anyLong(), - Mockito.any(Account.class))).thenReturn(false); + Mockito.when(accountDaoMock.findById(42l)).thenReturn(account); + Mockito.when(securityChecker.checkAccess(Mockito.any(Account.class), Mockito.any(ControlledEntity.class), Mockito.any(AccessType.class), Mockito.anyString())).thenReturn(true); + Mockito.when(accountDaoMock.remove(42l)).thenReturn(true); + Mockito.when(_configMgr.releaseAccountSpecificVirtualRanges(42l)).thenReturn(true); + Mockito.when(_userVmDao.listByAccountId(42l)).thenReturn(Arrays.asList(Mockito.mock(UserVmVO.class))); + Mockito.when(_vmMgr.expunge(Mockito.any(UserVmVO.class), Mockito.anyLong(), Mockito.any(Account.class))).thenReturn(false); Mockito.when(_domainMgr.getDomain(Mockito.anyLong())).thenReturn(domain); - Mockito.when( - securityChecker.checkAccess(Mockito.any(Account.class), - Mockito.any(Domain.class))) - .thenReturn(true); + Mockito.when(securityChecker.checkAccess(Mockito.any(Account.class), Mockito.any(Domain.class))).thenReturn(true); - Assert.assertTrue(accountManager.deleteUserAccount(42)); + Assert.assertTrue(accountManagerImpl.deleteUserAccount(42)); // assert that this was NOT a clean delete - Mockito.verify(_accountDao, Mockito.atLeastOnce()).markForCleanup( - Mockito.eq(42l)); + Mockito.verify(accountDaoMock, Mockito.atLeastOnce()).markForCleanup(Mockito.eq(42l)); } - @Test public void testAuthenticateUser() throws UnknownHostException { Pair<Boolean, UserAuthenticator.ActionOnFailedAuthentication> successAuthenticationPair = new Pair<>(true, null); @@ -160,21 +171,21 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase { UserAccountVO userAccountVO = new UserAccountVO(); userAccountVO.setSource(User.Source.UNKNOWN); userAccountVO.setState(Account.State.disabled.toString()); - Mockito.when(_userAccountDao.getUserAccount("test", 1L)).thenReturn(userAccountVO); + Mockito.when(userAccountDaoMock.getUserAccount("test", 1L)).thenReturn(userAccountVO); Mockito.when(userAuthenticator.authenticate("test", "fail", 1L, null)).thenReturn(failureAuthenticationPair); Mockito.when(userAuthenticator.authenticate("test", null, 1L, null)).thenReturn(successAuthenticationPair); Mockito.when(userAuthenticator.authenticate("test", "", 1L, null)).thenReturn(successAuthenticationPair); //Test for incorrect password. authentication should fail - UserAccount userAccount = accountManager.authenticateUser("test", "fail", 1L, InetAddress.getByName("127.0.0.1"), null); + UserAccount userAccount = accountManagerImpl.authenticateUser("test", "fail", 1L, InetAddress.getByName("127.0.0.1"), null); Assert.assertNull(userAccount); //Test for null password. authentication should fail - userAccount = accountManager.authenticateUser("test", null, 1L, InetAddress.getByName("127.0.0.1"), null); + userAccount = accountManagerImpl.authenticateUser("test", null, 1L, InetAddress.getByName("127.0.0.1"), null); Assert.assertNull(userAccount); //Test for empty password. authentication should fail - userAccount = accountManager.authenticateUser("test", "", 1L, InetAddress.getByName("127.0.0.1"), null); + userAccount = accountManagerImpl.authenticateUser("test", "", 1L, InetAddress.getByName("127.0.0.1"), null); Assert.assertNull(userAccount); //Verifying that the authentication method is only called when password is specified @@ -183,38 +194,509 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase { Mockito.verify(userAuthenticator, Mockito.never()).authenticate("test", "", 1L, null); } - @Mock - AccountVO callingAccount; - @Mock - DomainChecker domainChecker; - @Mock - AccountService accountService; - @Mock - private GetUserKeysCmd _listkeyscmd; - @Mock - private Account _account; - @Mock - private User _user; - @Mock - private UserAccountVO userAccountVO; - - - @Test (expected = PermissionDeniedException.class) - public void testgetUserCmd(){ + @Test(expected = PermissionDeniedException.class) + public void testgetUserCmd() { CallContext.register(callingUser, callingAccount); // Calling account is user account i.e normal account Mockito.when(_listkeyscmd.getID()).thenReturn(1L); - Mockito.when(accountManager.getActiveUser(1L)).thenReturn(_user); - Mockito.when(accountManager.getUserAccountById(1L)).thenReturn(userAccountVO); + Mockito.when(accountManagerImpl.getActiveUser(1L)).thenReturn(_user); + Mockito.when(accountManagerImpl.getUserAccountById(1L)).thenReturn(userAccountVO); Mockito.when(userAccountVO.getAccountId()).thenReturn(1L); - Mockito.when(accountManager.getAccount(Mockito.anyLong())).thenReturn(_account); // Queried account - admin account + Mockito.when(accountManagerImpl.getAccount(Mockito.anyLong())).thenReturn(accountMock); // Queried account - admin account Mockito.when(callingUser.getAccountId()).thenReturn(1L); - Mockito.when(_accountDao.findById(1L)).thenReturn(callingAccount); + Mockito.when(accountDaoMock.findById(1L)).thenReturn(callingAccount); Mockito.when(accountService.isNormalUser(Mockito.anyLong())).thenReturn(Boolean.TRUE); - Mockito.when(_account.getAccountId()).thenReturn(2L); + Mockito.when(accountMock.getAccountId()).thenReturn(2L); + + accountManagerImpl.getKeys(_listkeyscmd); + } + + @Test + public void updateUserTestTimeZoneAndEmailNull() { + prepareMockAndExecuteUpdateUserTest(0); + } + + @Test + public void updateUserTestTimeZoneAndEmailNotNull() { + Mockito.when(UpdateUserCmdMock.getEmail()).thenReturn("email"); + Mockito.when(UpdateUserCmdMock.getTimezone()).thenReturn("timezone"); + prepareMockAndExecuteUpdateUserTest(1); + } + + private void prepareMockAndExecuteUpdateUserTest(int numberOfExpectedCallsForSetEmailAndSetTimeZone) { + Mockito.doReturn(userVoMock).when(accountManagerImpl).retrieveAndValidateUser(UpdateUserCmdMock); + Mockito.doNothing().when(accountManagerImpl).validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock); + Mockito.doReturn(accountMock).when(accountManagerImpl).retrieveAndValidateAccount(userVoMock); + + Mockito.doNothing().when(accountManagerImpl).validateAndUpdateFirstNameIfNeeded(UpdateUserCmdMock, userVoMock); + Mockito.doNothing().when(accountManagerImpl).validateAndUpdateLastNameIfNeeded(UpdateUserCmdMock, userVoMock); + Mockito.doNothing().when(accountManagerImpl).validateAndUpdateUsernameIfNeeded(UpdateUserCmdMock, userVoMock, accountMock); + Mockito.doNothing().when(accountManagerImpl).validateUserPasswordAndUpdateIfNeeded(Mockito.anyString(), Mockito.eq(userVoMock), Mockito.anyString()); + + Mockito.doReturn(true).when(userDaoMock).update(Mockito.anyLong(), Mockito.eq(userVoMock)); + Mockito.doReturn(Mockito.mock(UserAccountVO.class)).when(userAccountDaoMock).findById(Mockito.anyLong()); + + accountManagerImpl.updateUser(UpdateUserCmdMock); + + InOrder inOrder = Mockito.inOrder(userVoMock, accountManagerImpl, userDaoMock, userAccountDaoMock); + + inOrder.verify(accountManagerImpl).retrieveAndValidateUser(UpdateUserCmdMock); + inOrder.verify(accountManagerImpl).validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock); + inOrder.verify(accountManagerImpl).retrieveAndValidateAccount(userVoMock); + + inOrder.verify(accountManagerImpl).validateAndUpdateFirstNameIfNeeded(UpdateUserCmdMock, userVoMock); + inOrder.verify(accountManagerImpl).validateAndUpdateLastNameIfNeeded(UpdateUserCmdMock, userVoMock); + inOrder.verify(accountManagerImpl).validateAndUpdateUsernameIfNeeded(UpdateUserCmdMock, userVoMock, accountMock); + inOrder.verify(accountManagerImpl).validateUserPasswordAndUpdateIfNeeded(Mockito.anyString(), Mockito.eq(userVoMock), Mockito.anyString()); + + inOrder.verify(userVoMock, Mockito.times(numberOfExpectedCallsForSetEmailAndSetTimeZone)).setEmail(Mockito.anyString()); + inOrder.verify(userVoMock, Mockito.times(numberOfExpectedCallsForSetEmailAndSetTimeZone)).setTimezone(Mockito.anyString()); + + inOrder.verify(userDaoMock).update(Mockito.anyLong(), Mockito.eq(userVoMock)); + inOrder.verify(userAccountDaoMock).findById(Mockito.anyLong()); + } + + @Test(expected = InvalidParameterValueException.class) + public void retrieveAndValidateUserTestNoUserFound() { + Mockito.doReturn(null).when(userDaoMock).getUser(Mockito.anyLong()); + + accountManagerImpl.retrieveAndValidateUser(UpdateUserCmdMock); + } + + @Test + public void retrieveAndValidateUserTestUserIsFound() { + Mockito.doReturn(userVoMock).when(userDaoMock).getUser(Mockito.anyLong()); + + UserVO receivedUser = accountManagerImpl.retrieveAndValidateUser(UpdateUserCmdMock); + + Assert.assertEquals(userVoMock, receivedUser); + } + + @Test + public void validateAndUpdatApiAndSecretKeyIfNeededTestNoKeys() { + accountManagerImpl.validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock); + + Mockito.verify(accountDaoMock, Mockito.times(0)).findUserAccountByApiKey(Mockito.anyString()); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateAndUpdatApiAndSecretKeyIfNeededTestOnlyApiKeyInformed() { + Mockito.doReturn("apiKey").when(UpdateUserCmdMock).getApiKey(); + + accountManagerImpl.validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateAndUpdatApiAndSecretKeyIfNeededTestOnlySecretKeyInformed() { + Mockito.doReturn("secretKey").when(UpdateUserCmdMock).getSecretKey(); + + accountManagerImpl.validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateAndUpdatApiAndSecretKeyIfNeededTestApiKeyAlreadyUsedBySomeoneElse() { + String apiKey = "apiKey"; + Mockito.doReturn(apiKey).when(UpdateUserCmdMock).getApiKey(); + Mockito.doReturn("secretKey").when(UpdateUserCmdMock).getSecretKey(); + + Mockito.doReturn(1L).when(userVoMock).getId(); + + User otherUserMock = Mockito.mock(User.class); + Mockito.doReturn(2L).when(otherUserMock).getId(); + + Pair<User, Account> pairUserAccountMock = new Pair<User, Account>(otherUserMock, Mockito.mock(Account.class)); + Mockito.doReturn(pairUserAccountMock).when(accountDaoMock).findUserAccountByApiKey(apiKey); + + accountManagerImpl.validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock); + } + + @Test + public void validateAndUpdatApiAndSecretKeyIfNeededTest() { + String apiKey = "apiKey"; + Mockito.doReturn(apiKey).when(UpdateUserCmdMock).getApiKey(); + + String secretKey = "secretKey"; + Mockito.doReturn(secretKey).when(UpdateUserCmdMock).getSecretKey(); + + Mockito.doReturn(1L).when(userVoMock).getId(); + + User otherUserMock = Mockito.mock(User.class); + Mockito.doReturn(1L).when(otherUserMock).getId(); + + Pair<User, Account> pairUserAccountMock = new Pair<User, Account>(otherUserMock, Mockito.mock(Account.class)); + Mockito.doReturn(pairUserAccountMock).when(accountDaoMock).findUserAccountByApiKey(apiKey); + + accountManagerImpl.validateAndUpdatApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock); + + Mockito.verify(accountDaoMock).findUserAccountByApiKey(apiKey); + Mockito.verify(userVoMock).setApiKey(apiKey); + Mockito.verify(userVoMock).setSecretKey(secretKey); + } + + @Test(expected = CloudRuntimeException.class) + public void retrieveAndValidateAccountTestAccountNotFound() { + Mockito.doReturn(accountMockId).when(userVoMock).getAccountId(); + + Mockito.doReturn(null).when(accountDaoMock).findById(accountMockId); + + accountManagerImpl.retrieveAndValidateAccount(userVoMock); + } + + @Test(expected = InvalidParameterValueException.class) + public void retrieveAndValidateAccountTestAccountTypeEqualsProjectType() { + Mockito.doReturn(accountMockId).when(userVoMock).getAccountId(); + Mockito.doReturn(Account.ACCOUNT_TYPE_PROJECT).when(accountMock).getType(); + Mockito.doReturn(accountMock).when(accountDaoMock).findById(accountMockId); + + accountManagerImpl.retrieveAndValidateAccount(userVoMock); + } + + @Test(expected = PermissionDeniedException.class) + public void retrieveAndValidateAccountTestAccountTypeEqualsSystemType() { + Mockito.doReturn(Account.ACCOUNT_ID_SYSTEM).when(userVoMock).getAccountId(); + Mockito.doReturn(Account.ACCOUNT_ID_SYSTEM).when(accountMock).getId(); + Mockito.doReturn(accountMock).when(accountDaoMock).findById(Account.ACCOUNT_ID_SYSTEM); + + accountManagerImpl.retrieveAndValidateAccount(userVoMock); + } + + @Test + public void retrieveAndValidateAccountTest() { + Mockito.doReturn(accountMockId).when(userVoMock).getAccountId(); + Mockito.doReturn(accountMock).when(accountDaoMock).findById(accountMockId); + + Mockito.doNothing().when(accountManagerImpl).checkAccess(Mockito.eq(accountMock), Mockito.eq(AccessType.OperateEntry), Mockito.anyBoolean(), Mockito.any(Account.class)); + accountManagerImpl.retrieveAndValidateAccount(userVoMock); + + Mockito.verify(accountManagerImpl).getCurrentCallingAccount(); + Mockito.verify(accountManagerImpl).checkAccess(Mockito.eq(accountMock), Mockito.eq(AccessType.OperateEntry), Mockito.anyBoolean(), Mockito.any(Account.class)); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateAndUpdateFirstNameIfNeededTestFirstNameBlank() { + Mockito.doReturn(" ").when(UpdateUserCmdMock).getFirstname(); + + accountManagerImpl.validateAndUpdateFirstNameIfNeeded(UpdateUserCmdMock, userVoMock); + } + + @Test + public void validateAndUpdateFirstNameIfNeededTestFirstNameNull() { + Mockito.doReturn(null).when(UpdateUserCmdMock).getFirstname(); + + accountManagerImpl.validateAndUpdateFirstNameIfNeeded(UpdateUserCmdMock, userVoMock); + + Mockito.verify(userVoMock, Mockito.times(0)).setFirstname(Mockito.anyString()); + } - accountManager.getKeys(_listkeyscmd); + @Test + public void validateAndUpdateFirstNameIfNeededTest() { + String firstname = "firstName"; + Mockito.doReturn(firstname).when(UpdateUserCmdMock).getFirstname(); + + accountManagerImpl.validateAndUpdateFirstNameIfNeeded(UpdateUserCmdMock, userVoMock); + + Mockito.verify(userVoMock).setFirstname(firstname); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateAndUpdateLastNameIfNeededTestLastNameBlank() { + Mockito.doReturn(" ").when(UpdateUserCmdMock).getLastname(); + + accountManagerImpl.validateAndUpdateLastNameIfNeeded(UpdateUserCmdMock, userVoMock); + } + + @Test + public void validateAndUpdateLastNameIfNeededTestLastNameNull() { + Mockito.doReturn(null).when(UpdateUserCmdMock).getLastname(); + + accountManagerImpl.validateAndUpdateLastNameIfNeeded(UpdateUserCmdMock, userVoMock); + + Mockito.verify(userVoMock, Mockito.times(0)).setLastname(Mockito.anyString()); + } + + @Test + public void validateAndUpdateLastNameIfNeededTest() { + String lastName = "lastName"; + Mockito.doReturn(lastName).when(UpdateUserCmdMock).getLastname(); + + accountManagerImpl.validateAndUpdateLastNameIfNeeded(UpdateUserCmdMock, userVoMock); + + Mockito.verify(userVoMock).setLastname(lastName); + } + + @Test + public void validateAndUpdateUsernameIfNeededTestNullUsername() { + Mockito.doReturn(null).when(UpdateUserCmdMock).getUsername(); + + accountManagerImpl.validateAndUpdateUsernameIfNeeded(UpdateUserCmdMock, userVoMock, accountMock); + + Mockito.verify(userVoMock, Mockito.times(0)).setUsername(Mockito.anyString()); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateAndUpdateUsernameIfNeededTestBlankUsername() { + Mockito.doReturn(" ").when(UpdateUserCmdMock).getUsername(); + + accountManagerImpl.validateAndUpdateUsernameIfNeeded(UpdateUserCmdMock, userVoMock, accountMock); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateAndUpdateUsernameIfNeededTestDuplicatedUserSameDomainThisUser() { + long domanIdCurrentUser = 22l; + + String userName = "username"; + Mockito.doReturn(userName).when(UpdateUserCmdMock).getUsername(); + Mockito.doReturn(userName).when(userVoMock).getUsername(); + Mockito.doReturn(domanIdCurrentUser).when(accountMock).getDomainId(); + + long userVoDuplicatedMockId = 67l; + UserVO userVoDuplicatedMock = Mockito.mock(UserVO.class); + Mockito.doReturn(userName).when(userVoDuplicatedMock).getUsername(); + Mockito.doReturn(userVoDuplicatedMockId).when(userVoDuplicatedMock).getId(); + + long accountIdUserDuplicated = 98l; + Mockito.doReturn(accountIdUserDuplicated).when(userVoDuplicatedMock).getAccountId(); + + Account accountUserDuplicatedMock = Mockito.mock(Account.class); + Mockito.doReturn(accountIdUserDuplicated).when(accountUserDuplicatedMock).getId(); + Mockito.doReturn(domanIdCurrentUser).when(accountUserDuplicatedMock).getDomainId(); + + List<UserVO> usersWithSameUserName = new ArrayList<>(); + usersWithSameUserName.add(userVoMock); + usersWithSameUserName.add(userVoDuplicatedMock); + + Mockito.doReturn(usersWithSameUserName).when(userDaoMock).findUsersByName(userName); + + Mockito.doReturn(accountMock).when(accountDaoMock).findById(accountMockId); + Mockito.doReturn(accountUserDuplicatedMock).when(accountDaoMock).findById(accountIdUserDuplicated); + + Mockito.doReturn(Mockito.mock(DomainVO.class)).when(_domainDao).findById(Mockito.anyLong()); + + accountManagerImpl.validateAndUpdateUsernameIfNeeded(UpdateUserCmdMock, userVoMock, accountMock); + } + + @Test + public void validateAndUpdateUsernameIfNeededTestDuplicatedUserButInDifferentDomains() { + long domanIdCurrentUser = 22l; + + String userName = "username"; + Mockito.doReturn(userName).when(UpdateUserCmdMock).getUsername(); + Mockito.doReturn(userName).when(userVoMock).getUsername(); + Mockito.doReturn(domanIdCurrentUser).when(accountMock).getDomainId(); + + long userVoDuplicatedMockId = 67l; + UserVO userVoDuplicatedMock = Mockito.mock(UserVO.class); + Mockito.doReturn(userName).when(userVoDuplicatedMock).getUsername(); + Mockito.doReturn(userVoDuplicatedMockId).when(userVoDuplicatedMock).getId(); + + long accountIdUserDuplicated = 98l; + Mockito.doReturn(accountIdUserDuplicated).when(userVoDuplicatedMock).getAccountId(); + + Account accountUserDuplicatedMock = Mockito.mock(Account.class); + Mockito.doReturn(accountIdUserDuplicated).when(accountUserDuplicatedMock).getId(); + Mockito.doReturn(45l).when(accountUserDuplicatedMock).getDomainId(); + + List<UserVO> usersWithSameUserName = new ArrayList<>(); + usersWithSameUserName.add(userVoMock); + usersWithSameUserName.add(userVoDuplicatedMock); + + Mockito.doReturn(usersWithSameUserName).when(userDaoMock).findUsersByName(userName); + + Mockito.doReturn(accountMock).when(accountDaoMock).findById(accountMockId); + Mockito.doReturn(accountUserDuplicatedMock).when(accountDaoMock).findById(accountIdUserDuplicated); + + accountManagerImpl.validateAndUpdateUsernameIfNeeded(UpdateUserCmdMock, userVoMock, accountMock); + + Mockito.verify(userVoMock).setUsername(userName); + } + + @Test + public void validateAndUpdateUsernameIfNeededTestNoDuplicatedUserNames() { + long domanIdCurrentUser = 22l; + + String userName = "username"; + Mockito.doReturn(userName).when(UpdateUserCmdMock).getUsername(); + Mockito.doReturn(userName).when(userVoMock).getUsername(); + Mockito.doReturn(domanIdCurrentUser).when(accountMock).getDomainId(); + + List<UserVO> usersWithSameUserName = new ArrayList<>(); + + Mockito.doReturn(usersWithSameUserName).when(userDaoMock).findUsersByName(userName); + + Mockito.doReturn(accountMock).when(accountDaoMock).findById(accountMockId); + + accountManagerImpl.validateAndUpdateUsernameIfNeeded(UpdateUserCmdMock, userVoMock, accountMock); + + Mockito.verify(userVoMock).setUsername(userName); + } + + @Test + public void valiateUserPasswordAndUpdateIfNeededTestPasswordNull() { + accountManagerImpl.validateUserPasswordAndUpdateIfNeeded(null, userVoMock, null); + + Mockito.verify(userVoMock, Mockito.times(0)).setPassword(Mockito.anyString()); + } + + @Test(expected = InvalidParameterValueException.class) + public void valiateUserPasswordAndUpdateIfNeededTestBlankPassword() { + accountManagerImpl.validateUserPasswordAndUpdateIfNeeded(" ", userVoMock, null); + } + + @Test(expected = InvalidParameterValueException.class) + public void valiateUserPasswordAndUpdateIfNeededTestNoAdminAndNoCurrentPasswordProvided() { + Mockito.doReturn(accountMock).when(accountManagerImpl).getCurrentCallingAccount(); + Mockito.doReturn(false).when(accountManagerImpl).isRootAdmin(accountMockId); + Mockito.doReturn(false).when(accountManagerImpl).isDomainAdmin(accountMockId); + Mockito.doReturn(true).when(accountManagerImpl).isResourceDomainAdmin(accountMockId); + + accountManagerImpl.validateUserPasswordAndUpdateIfNeeded("newPassword", userVoMock, " "); + } + + @Test(expected = CloudRuntimeException.class) + public void valiateUserPasswordAndUpdateIfNeededTestNoUserAuthenticatorsConfigured() { + Mockito.doReturn(accountMock).when(accountManagerImpl).getCurrentCallingAccount(); + Mockito.doReturn(true).when(accountManagerImpl).isRootAdmin(accountMockId); + Mockito.doReturn(false).when(accountManagerImpl).isDomainAdmin(accountMockId); + + Mockito.doNothing().when(accountManagerImpl).validateCurrentPassword(Mockito.eq(userVoMock), Mockito.anyString()); + + accountManagerImpl.validateUserPasswordAndUpdateIfNeeded("newPassword", userVoMock, null); + } + + @Test + public void validateUserPasswordAndUpdateIfNeededTestRootAdminUpdatingUserPassword() { + Mockito.doReturn(accountMock).when(accountManagerImpl).getCurrentCallingAccount(); + Mockito.doReturn(true).when(accountManagerImpl).isRootAdmin(accountMockId); + Mockito.doReturn(false).when(accountManagerImpl).isDomainAdmin(accountMockId); + + String newPassword = "newPassword"; + + String expectedUserPasswordAfterEncoded = configureUserMockAuthenticators(newPassword); + + Mockito.doNothing().when(accountManagerImpl).validateCurrentPassword(Mockito.eq(userVoMock), Mockito.anyString()); + + accountManagerImpl.validateUserPasswordAndUpdateIfNeeded(newPassword, userVoMock, null); + + Mockito.verify(accountManagerImpl, Mockito.times(0)).validateCurrentPassword(Mockito.eq(userVoMock), Mockito.anyString()); + Mockito.verify(userVoMock, Mockito.times(1)).setPassword(expectedUserPasswordAfterEncoded); + } + + @Test + public void validateUserPasswordAndUpdateIfNeededTestDomainAdminUpdatingUserPassword() { + Mockito.doReturn(accountMock).when(accountManagerImpl).getCurrentCallingAccount(); + Mockito.doReturn(false).when(accountManagerImpl).isRootAdmin(accountMockId); + Mockito.doReturn(true).when(accountManagerImpl).isDomainAdmin(accountMockId); + + String newPassword = "newPassword"; + + String expectedUserPasswordAfterEncoded = configureUserMockAuthenticators(newPassword); + + Mockito.doNothing().when(accountManagerImpl).validateCurrentPassword(Mockito.eq(userVoMock), Mockito.anyString()); + + accountManagerImpl.validateUserPasswordAndUpdateIfNeeded(newPassword, userVoMock, null); + + Mockito.verify(accountManagerImpl, Mockito.times(0)).validateCurrentPassword(Mockito.eq(userVoMock), Mockito.anyString()); + Mockito.verify(userVoMock, Mockito.times(1)).setPassword(expectedUserPasswordAfterEncoded); + } + + @Test + public void validateUserPasswordAndUpdateIfNeededTestUserUpdatingHisPassword() { + Mockito.doReturn(accountMock).when(accountManagerImpl).getCurrentCallingAccount(); + Mockito.doReturn(false).when(accountManagerImpl).isRootAdmin(accountMockId); + Mockito.doReturn(false).when(accountManagerImpl).isDomainAdmin(accountMockId); + + String newPassword = "newPassword"; + String expectedUserPasswordAfterEncoded = configureUserMockAuthenticators(newPassword); + + Mockito.doNothing().when(accountManagerImpl).validateCurrentPassword(Mockito.eq(userVoMock), Mockito.anyString()); + + String currentPassword = "theCurrentPassword"; + accountManagerImpl.validateUserPasswordAndUpdateIfNeeded(newPassword, userVoMock, currentPassword); + + Mockito.verify(accountManagerImpl, Mockito.times(1)).validateCurrentPassword(userVoMock, currentPassword); + Mockito.verify(userVoMock, Mockito.times(1)).setPassword(expectedUserPasswordAfterEncoded); + } + + private String configureUserMockAuthenticators(String newPassword) { + accountManagerImpl._userPasswordEncoders = new ArrayList<>(); + UserAuthenticator authenticatorMock1 = Mockito.mock(UserAuthenticator.class); + String expectedUserPasswordAfterEncoded = "passwordEncodedByAuthenticator1"; + Mockito.doReturn(expectedUserPasswordAfterEncoded).when(authenticatorMock1).encode(newPassword); + + UserAuthenticator authenticatorMock2 = Mockito.mock(UserAuthenticator.class); + Mockito.doReturn("passwordEncodedByAuthenticator2").when(authenticatorMock2).encode(newPassword); + + accountManagerImpl._userPasswordEncoders.add(authenticatorMock1); + accountManagerImpl._userPasswordEncoders.add(authenticatorMock2); + return expectedUserPasswordAfterEncoded; + } + + @Test(expected = InvalidParameterValueException.class) + public void validateCurrentPasswordTestUserNotAuthenticatedWithProvidedCurrentPassword() { + Mockito.doReturn(Mockito.mock(AccountVO.class)).when(accountDaoMock).findById(accountMockId); + String newPassword = "newPassword"; + configureUserMockAuthenticators(newPassword); + + accountManagerImpl.validateCurrentPassword(userVoMock, "currentPassword"); + } + + @Test + public void validateCurrentPasswordTestUserAuthenticatedWithProvidedCurrentPasswordViaFirstAuthenticator() { + AccountVO accountVoMock = Mockito.mock(AccountVO.class); + long domainId = 14l; + Mockito.doReturn(domainId).when(accountVoMock).getDomainId(); + + Mockito.doReturn(accountVoMock).when(accountDaoMock).findById(accountMockId); + String username = "username"; + Mockito.doReturn(username).when(userVoMock).getUsername(); + + accountManagerImpl._userPasswordEncoders = new ArrayList<>(); + UserAuthenticator authenticatorMock1 = Mockito.mock(UserAuthenticator.class); + UserAuthenticator authenticatorMock2 = Mockito.mock(UserAuthenticator.class); + + accountManagerImpl._userPasswordEncoders.add(authenticatorMock1); + accountManagerImpl._userPasswordEncoders.add(authenticatorMock2); + + Pair<Boolean, ActionOnFailedAuthentication> authenticationResult = new Pair<Boolean, UserAuthenticator.ActionOnFailedAuthentication>(true, + UserAuthenticator.ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT); + + String currentPassword = "currentPassword"; + Mockito.doReturn(authenticationResult).when(authenticatorMock1).authenticate(username, currentPassword, domainId, null); + + accountManagerImpl.validateCurrentPassword(userVoMock, currentPassword); + + Mockito.verify(authenticatorMock1, Mockito.times(1)).authenticate(username, currentPassword, domainId, null); + Mockito.verify(authenticatorMock2, Mockito.times(0)).authenticate(username, currentPassword, domainId, null); + } + + @Test + public void validateCurrentPasswordTestUserAuthenticatedWithProvidedCurrentPasswordViaSecondAuthenticator() { + AccountVO accountVoMock = Mockito.mock(AccountVO.class); + long domainId = 14l; + Mockito.doReturn(domainId).when(accountVoMock).getDomainId(); + + Mockito.doReturn(accountVoMock).when(accountDaoMock).findById(accountMockId); + String username = "username"; + Mockito.doReturn(username).when(userVoMock).getUsername(); + + accountManagerImpl._userPasswordEncoders = new ArrayList<>(); + UserAuthenticator authenticatorMock1 = Mockito.mock(UserAuthenticator.class); + UserAuthenticator authenticatorMock2 = Mockito.mock(UserAuthenticator.class); + + accountManagerImpl._userPasswordEncoders.add(authenticatorMock1); + accountManagerImpl._userPasswordEncoders.add(authenticatorMock2); + + Pair<Boolean, ActionOnFailedAuthentication> authenticationResult = new Pair<Boolean, UserAuthenticator.ActionOnFailedAuthentication>(true, + UserAuthenticator.ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT); + + String currentPassword = "currentPassword"; + Mockito.doReturn(authenticationResult).when(authenticatorMock2).authenticate(username, currentPassword, domainId, null); + + accountManagerImpl.validateCurrentPassword(userVoMock, currentPassword); + + Mockito.verify(authenticatorMock1, Mockito.times(1)).authenticate(username, currentPassword, domainId, null); + Mockito.verify(authenticatorMock2, Mockito.times(1)).authenticate(username, currentPassword, domainId, null); + } - } } diff --git a/server/src/test/java/com/cloud/user/AccountManagerImplVolumeDeleteEventTest.java b/server/src/test/java/com/cloud/user/AccountManagerImplVolumeDeleteEventTest.java index 2397713..48eb473 100644 --- a/server/src/test/java/com/cloud/user/AccountManagerImplVolumeDeleteEventTest.java +++ b/server/src/test/java/com/cloud/user/AccountManagerImplVolumeDeleteEventTest.java @@ -16,12 +16,12 @@ // under the License. package com.cloud.user; -import static org.mockito.Mockito.when; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.anyBoolean; +import static org.mockito.Mockito.when; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -52,13 +52,12 @@ import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.CloudException; import com.cloud.exception.ConcurrentOperationException; import com.cloud.service.ServiceOfferingVO; -import com.cloud.storage.VolumeVO; import com.cloud.storage.Volume.Type; +import com.cloud.storage.VolumeVO; import com.cloud.vm.UserVmManagerImpl; import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine; - public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplTestBase { private static final Long ACCOUNT_ID = 1l; @@ -70,12 +69,10 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT Map<String, Object> oldFields = new HashMap<>(); UserVmVO vm = mock(UserVmVO.class); - // Configures the static fields of the UsageEventUtils class, Storing the // previous values to be restored during the cleanup phase, to avoid // interference with other unit tests. - protected UsageEventUtils setupUsageUtils() throws NoSuchMethodException, SecurityException, IllegalAccessException, - IllegalArgumentException, InvocationTargetException { + protected UsageEventUtils setupUsageUtils() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { _usageEventDao = new MockUsageEventDao(); UsageEventUtils utils = new UsageEventUtils(); @@ -93,8 +90,7 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT Field staticField = UsageEventUtils.class.getDeclaredField("s_" + fieldName); staticField.setAccessible(true); oldFields.put(f.getName(), staticField.get(null)); - f.set(utils, - this.getClass().getSuperclass().getDeclaredField("_" + fieldName).get(this)); + f.set(utils, this.getClass().getSuperclass().getDeclaredField("_" + fieldName).get(this)); } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { e.printStackTrace(); } @@ -106,14 +102,12 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT return utils; } - - protected void defineMocksBehavior() - throws AgentUnavailableException, ConcurrentOperationException, CloudException { + protected void defineMocksBehavior() throws AgentUnavailableException, ConcurrentOperationException, CloudException { AccountVO account = new AccountVO(); account.setId(ACCOUNT_ID); - when(_accountDao.remove(ACCOUNT_ID)).thenReturn(true); - when(_accountDao.findById(ACCOUNT_ID)).thenReturn(account); + when(accountDaoMock.remove(ACCOUNT_ID)).thenReturn(true); + when(accountDaoMock.findById(ACCOUNT_ID)).thenReturn(account); DomainVO domain = new DomainVO(); VirtualMachineEntity vmEntity = mock(VirtualMachineEntity.class); @@ -128,8 +122,7 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT List<VolumeVO> volumes = new ArrayList<>(); volumes.add(vol); - when(securityChecker.checkAccess(any(Account.class), any(ControlledEntity.class), any(AccessType.class), - anyString())).thenReturn(true); + when(securityChecker.checkAccess(any(Account.class), any(ControlledEntity.class), any(AccessType.class), anyString())).thenReturn(true); when(_userVmDao.findById(anyLong())).thenReturn(vm); when(_userVmDao.listByAccountId(ACCOUNT_ID)).thenReturn(Arrays.asList(vm)); @@ -142,8 +135,7 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT when(offering.getId()).thenReturn(1l); when(offering.getCpu()).thenReturn(500); when(offering.getRamSize()).thenReturn(500); - when(_serviceOfferingDao.findByIdIncludingRemoved(anyLong(), anyLong())) - .thenReturn(offering); + when(_serviceOfferingDao.findByIdIncludingRemoved(anyLong(), anyLong())).thenReturn(offering); when(_domainMgr.getDomain(anyLong())).thenReturn(domain); @@ -152,9 +144,8 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT } @Before - public void init() - throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, - InvocationTargetException, AgentUnavailableException, ConcurrentOperationException, CloudException { + public void init() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, AgentUnavailableException, + ConcurrentOperationException, CloudException { setupUsageUtils(); defineMocksBehavior(); @@ -175,22 +166,19 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT method.invoke(utils); } - @SuppressWarnings("unchecked") - protected List<UsageEventVO> deleteUserAccountRootVolumeUsageEvents(boolean vmDestroyedPrior) - throws AgentUnavailableException, ConcurrentOperationException, CloudException { + protected List<UsageEventVO> deleteUserAccountRootVolumeUsageEvents(boolean vmDestroyedPrior) throws AgentUnavailableException, ConcurrentOperationException, CloudException { - when(vm.getState()) - .thenReturn(vmDestroyedPrior ? VirtualMachine.State.Destroyed : VirtualMachine.State.Running); + when(vm.getState()).thenReturn(vmDestroyedPrior ? VirtualMachine.State.Destroyed : VirtualMachine.State.Running); when(vm.getRemoved()).thenReturn(vmDestroyedPrior ? new Date() : null); - accountManager.deleteUserAccount(ACCOUNT_ID); + accountManagerImpl.deleteUserAccount(ACCOUNT_ID); return _usageEventDao.listAll(); } @Test // If the VM is alerady destroyed, no events should get emitted - public void destroyedVMRootVolumeUsageEvent() throws SecurityException, IllegalArgumentException, - ReflectiveOperationException, AgentUnavailableException, ConcurrentOperationException, CloudException { + public void destroyedVMRootVolumeUsageEvent() + throws SecurityException, IllegalArgumentException, ReflectiveOperationException, AgentUnavailableException, ConcurrentOperationException, CloudException { List<UsageEventVO> emittedEvents = deleteUserAccountRootVolumeUsageEvents(true); Assert.assertEquals(0, emittedEvents.size()); } @@ -198,8 +186,8 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT @Test // If the VM is running, we should see one emitted event for the root // volume. - public void runningVMRootVolumeUsageEvent() throws SecurityException, IllegalArgumentException, - ReflectiveOperationException, AgentUnavailableException, ConcurrentOperationException, CloudException { + public void runningVMRootVolumeUsageEvent() + throws SecurityException, IllegalArgumentException, ReflectiveOperationException, AgentUnavailableException, ConcurrentOperationException, CloudException { List<UsageEventVO> emittedEvents = deleteUserAccountRootVolumeUsageEvents(false); Assert.assertEquals(1, emittedEvents.size()); UsageEventVO event = emittedEvents.get(0); diff --git a/server/src/test/java/com/cloud/user/AccountManagetImplTestBase.java b/server/src/test/java/com/cloud/user/AccountManagetImplTestBase.java index 53b781a..885a23d 100644 --- a/server/src/test/java/com/cloud/user/AccountManagetImplTestBase.java +++ b/server/src/test/java/com/cloud/user/AccountManagetImplTestBase.java @@ -21,8 +21,6 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import javax.inject.Inject; - import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.affinity.dao.AffinityGroupDao; import org.apache.cloudstack.context.CallContext; @@ -34,9 +32,10 @@ import org.apache.cloudstack.region.gslb.GlobalLoadBalancerRuleDao; import org.junit.After; import org.junit.Before; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.runners.MockitoJUnitRunner; -import org.springframework.test.util.ReflectionTestUtils; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.dao.ResourceCountDao; @@ -84,17 +83,17 @@ import com.cloud.vm.snapshot.dao.VMSnapshotDao; public class AccountManagetImplTestBase { @Mock - AccountDao _accountDao; + AccountDao accountDaoMock; @Mock ConfigurationDao _configDao; @Mock ResourceCountDao _resourceCountDao; @Mock - UserDao _userDao; + UserDao userDaoMock; @Mock InstanceGroupDao _vmGroupDao; @Mock - UserAccountDao _userAccountDao; + UserAccountDao userAccountDaoMock; @Mock VolumeDao _volumeDao; @Mock @@ -193,27 +192,16 @@ public class AccountManagetImplTestBase { @Mock SSHKeyPairDao _sshKeyPairDao; - AccountManagerImpl accountManager; - - UsageEventDao _usageEventDao = new MockUsageEventDao(); + @Spy + @InjectMocks + AccountManagerImpl accountManagerImpl; + @Mock + UsageEventDao _usageEventDao; @Before - public void setup() - throws NoSuchFieldException, SecurityException, - IllegalArgumentException, IllegalAccessException { - accountManager = new AccountManagerImpl(); - Map<String, Field> declaredFields = getInheritedFields(this.getClass()); - for (Field field : AccountManagerImpl.class.getDeclaredFields()) { - if (field.getAnnotation(Inject.class) != null) { - field.setAccessible(true); - if (declaredFields.containsKey(field.getName())) { - Field mockField = declaredFields.get(field.getName()); - field.set(accountManager, mockField.get(this)); - } - } - } - ReflectionTestUtils.setField(accountManager, "_userAuthenticators", Arrays.asList(userAuthenticator)); - accountManager.setSecurityCheckers(Arrays.asList(securityChecker)); + public void setup() { + accountManagerImpl.setUserAuthenticators(Arrays.asList(userAuthenticator)); + accountManagerImpl.setSecurityCheckers(Arrays.asList(securityChecker)); CallContext.register(callingUser, callingAccount); } @@ -231,14 +219,4 @@ public class AccountManagetImplTestBase { } return fields; } - - public static Map<Class<?>, Field> getInheritedFieldsByClass(Class<?> type) { - Map<Class<?>, Field> fields = new HashMap<>(); - for (Class<?> c = type; c != null; c = c.getSuperclass()) { - for (Field f : c.getDeclaredFields()) { - fields.put(f.getType(), f); - } - } - return fields; - } } diff --git a/server/src/test/java/com/cloud/user/MockAccountManagerImpl.java b/server/src/test/java/com/cloud/user/MockAccountManagerImpl.java index 0a92c14..4fbf752 100644 --- a/server/src/test/java/com/cloud/user/MockAccountManagerImpl.java +++ b/server/src/test/java/com/cloud/user/MockAccountManagerImpl.java @@ -158,13 +158,6 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco } @Override - public UserAccount updateUser(Long userId, String firstName, String lastName, String email, String userName, String password, String apiKey, String secretKey, - String timeZone) { - // TODO Auto-generated method stub - return null; - } - - @Override public Account getActiveAccountById(long accountId) { // TODO Auto-generated method stub return null; diff --git a/server/src/test/java/com/cloud/user/MockDomainManagerImpl.java b/server/src/test/java/com/cloud/user/MockDomainManagerImpl.java deleted file mode 100644 index 394c3e2..0000000 --- a/server/src/test/java/com/cloud/user/MockDomainManagerImpl.java +++ /dev/null @@ -1,164 +0,0 @@ -// 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 com.cloud.user; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.naming.ConfigurationException; - -import org.springframework.stereotype.Component; - -import org.apache.cloudstack.api.command.admin.domain.ListDomainChildrenCmd; -import org.apache.cloudstack.api.command.admin.domain.ListDomainsCmd; -import org.apache.cloudstack.api.command.admin.domain.UpdateDomainCmd; - -import com.cloud.domain.Domain; -import com.cloud.domain.DomainVO; -import com.cloud.exception.PermissionDeniedException; -import com.cloud.utils.Pair; -import com.cloud.utils.component.ManagerBase; - -@Component -public class MockDomainManagerImpl extends ManagerBase implements DomainManager, DomainService { - - @Override - public Domain getDomain(long id) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Domain getDomain(String uuid) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Domain getDomainByName(String name, long parentId) { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean isChildDomain(Long parentId, Long childId) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean deleteDomain(long domainId, Boolean cleanup) { - // TODO Auto-generated method stub - return false; - } - - @Override - public Pair<List<? extends Domain>, Integer> searchForDomains(ListDomainsCmd cmd) throws PermissionDeniedException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Pair<List<? extends Domain>, Integer> searchForDomainChildren(ListDomainChildrenCmd cmd) throws PermissionDeniedException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Set<Long> getDomainChildrenIds(String parentDomainPath) { - // TODO Auto-generated method stub - return null; - } - - @Override - public DomainVO findDomainByPath(String domainPath) { - // TODO Auto-generated method stub - return null; - } - - @Override - public DomainVO findDomainByIdOrPath(Long id, String domainPath) { - return null; - } - - @Override - public Set<Long> getDomainParentIds(long domainId) { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean removeDomain(long domainId) { - // TODO Auto-generated method stub - return false; - } - - @Override - public List<? extends Domain> findInactiveDomains() { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean deleteDomain(DomainVO domain, Boolean cleanup) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { - return true; - } - - @Override - public boolean start() { - // TODO Auto-generated method stub - return true; - } - - @Override - public boolean stop() { - // TODO Auto-generated method stub - return true; - } - - @Override - public String getName() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Domain createDomain(String name, Long parentId, String networkDomain, String domainUUID) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Domain updateDomain(UpdateDomainCmd cmd) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Domain createDomain(String name, Long parentId, Long ownerId, String networkDomain, String domainUUID) { - // TODO Auto-generated method stub - return null; - } - -} diff --git a/ui/l10n/ar.js b/ui/l10n/ar.js index 82317f4..87c1193 100644 --- a/ui/l10n/ar.js +++ b/ui/l10n/ar.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "الشبكات", "label.new": "جديد", "label.new.password": "New Password", + "label.current.password": "Current Password", "label.new.project": "مشروع جديد", "label.new.ssh.key.pair": "New SSH Key Pair", "label.new.vm": "New VM", diff --git a/ui/l10n/ca.js b/ui/l10n/ca.js index 4eff532..2580301 100644 --- a/ui/l10n/ca.js +++ b/ui/l10n/ca.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "Xarxes", "label.new": "Nou", "label.new.password": "New Password", + "label.current.password": "Current Password", "label.new.project": "Nou projecte", "label.new.ssh.key.pair": "New SSH Key Pair", "label.new.vm": "Nova MV", diff --git a/ui/l10n/de_DE.js b/ui/l10n/de_DE.js index 696f466..e09062f 100644 --- a/ui/l10n/de_DE.js +++ b/ui/l10n/de_DE.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "Netzwerke", "label.new": "Neu", "label.new.password": "Neues Passwort", + "label.current.password": "Current Password", "label.new.project": "Neues Projekt", "label.new.ssh.key.pair": "Neues SSH-Schlüsselpaar", "label.new.vm": "Neue VM", diff --git a/ui/l10n/en.js b/ui/l10n/en.js index e647f9a..e16031d 100644 --- a/ui/l10n/en.js +++ b/ui/l10n/en.js @@ -1186,6 +1186,7 @@ var dictionary = { "label.networks":"Networks", "label.new":"New", "label.new.password":"New Password", +"label.current.password": "Current Password", "label.new.project":"New Project", "label.new.ssh.key.pair":"New SSH Key Pair", "label.new.vm":"New VM", diff --git a/ui/l10n/es.js b/ui/l10n/es.js index 8713783..285df3d 100644 --- a/ui/l10n/es.js +++ b/ui/l10n/es.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "Redes", "label.new": "Nuevo", "label.new.password": "Nueva contraseña", + "label.current.password": "Current Password", "label.new.project": "Nuevo Proyecto", "label.new.ssh.key.pair": "Nuevo Par de Claves SSH", "label.new.vm": "Nueva MV", diff --git a/ui/l10n/fr_FR.js b/ui/l10n/fr_FR.js index 2236457..8ecf80d 100644 --- a/ui/l10n/fr_FR.js +++ b/ui/l10n/fr_FR.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "Réseaux", "label.new": "Nouveau", "label.new.password": "Nouveau mot de passe", + "label.current.password": "Current Password", "label.new.project": "Nouveau projet", "label.new.ssh.key.pair": "Nouvelle bi-clé SSH", "label.new.vm": "Nouvelle VM", diff --git a/ui/l10n/hu.js b/ui/l10n/hu.js index 49cfaaf..2ed1f34 100644 --- a/ui/l10n/hu.js +++ b/ui/l10n/hu.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "Hálózatok", "label.new": "Új", "label.new.password": "Új jelszó", + "label.current.password": "Current Password", "label.new.project": "Új projekt", "label.new.ssh.key.pair": "Új SSH kulcspár", "label.new.vm": "Új VM", diff --git a/ui/l10n/it_IT.js b/ui/l10n/it_IT.js index 06d85a8..c2210ca 100644 --- a/ui/l10n/it_IT.js +++ b/ui/l10n/it_IT.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "Reti", "label.new": "Nuovo", "label.new.password": "New Password", + "label.current.password": "Current Password", "label.new.project": "Nuovo Progetto", "label.new.ssh.key.pair": "New SSH Key Pair", "label.new.vm": "Nuova VM", diff --git a/ui/l10n/ja_JP.js b/ui/l10n/ja_JP.js index 674e8ed..db94ca8 100644 --- a/ui/l10n/ja_JP.js +++ b/ui/l10n/ja_JP.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "ネットワーク", "label.new": "新規", "label.new.password": "新しいパスワード", + "label.current.password": "Current Password", "label.new.project": "新しいプロジェクト", "label.new.ssh.key.pair": "新しい SSH キーペア", "label.new.vm": "新しい VM", diff --git a/ui/l10n/ko_KR.js b/ui/l10n/ko_KR.js index 42d886f..52cd183 100644 --- a/ui/l10n/ko_KR.js +++ b/ui/l10n/ko_KR.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "네트워크", "label.new": "신규", "label.new.password": "새로운 암호", + "label.current.password": "Current Password", "label.new.project": "새 프로젝트", "label.new.ssh.key.pair": "New SSH Key Pair", "label.new.vm": "새 VM", diff --git a/ui/l10n/nb_NO.js b/ui/l10n/nb_NO.js index df22f16..fbe3bba 100644 --- a/ui/l10n/nb_NO.js +++ b/ui/l10n/nb_NO.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "Nettverk", "label.new": "Ny", "label.new.password": "Nytt passord", + "label.current.password": "Current Password", "label.new.project": "Nytt prosjekt", "label.new.ssh.key.pair": "Nytt SSH-nøkkelpar", "label.new.vm": "Ny VM", diff --git a/ui/l10n/nl_NL.js b/ui/l10n/nl_NL.js index cffcb62..d1a96da 100644 --- a/ui/l10n/nl_NL.js +++ b/ui/l10n/nl_NL.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "Netwerken", "label.new": "Nieuw", "label.new.password": "Nieuw wachtwoord", + "label.current.password": "Current Password", "label.new.project": "Nieuw Project", "label.new.ssh.key.pair": "nieuw SSH sleutelpaar", "label.new.vm": "Nieuwe VM", diff --git a/ui/l10n/pl.js b/ui/l10n/pl.js index 3828cbc..ccac03c 100644 --- a/ui/l10n/pl.js +++ b/ui/l10n/pl.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "Sieci", "label.new": "Nowy", "label.new.password": "New Password", + "label.current.password": "Current Password", "label.new.project": "Nowy projekt", "label.new.ssh.key.pair": "New SSH Key Pair", "label.new.vm": "New VM", diff --git a/ui/l10n/pt_BR.js b/ui/l10n/pt_BR.js index 0082736..06b8553 100644 --- a/ui/l10n/pt_BR.js +++ b/ui/l10n/pt_BR.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "Redes", "label.new": "Novo", "label.new.password": "Nova Senha", + "label.current.password": "Senha Antiga", "label.new.project": "Novo Projeto", "label.new.ssh.key.pair": "Novo par de chaves SSH", "label.new.vm": "Nova VM", diff --git a/ui/l10n/ru_RU.js b/ui/l10n/ru_RU.js index 01d9821..a61fe1e 100644 --- a/ui/l10n/ru_RU.js +++ b/ui/l10n/ru_RU.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "Сети", "label.new": "Создать", "label.new.password": "Новый пароль", + "label.current.password": "Current Password", "label.new.project": "Новый проект", "label.new.ssh.key.pair": "New SSH Key Pair", "label.new.vm": "Новая ВМ", diff --git a/ui/l10n/zh_CN.js b/ui/l10n/zh_CN.js index 9ddea35..6015f41 100644 --- a/ui/l10n/zh_CN.js +++ b/ui/l10n/zh_CN.js @@ -1151,6 +1151,7 @@ var dictionary = { "label.networks": "网络", "label.new": "新建", "label.new.password": "新密码", + "label.current.password": "Current Password", "label.new.project": "新建项目", "label.new.ssh.key.pair": "新SSH密钥对", "label.new.vm": "新建 VM", diff --git a/ui/scripts/accounts.js b/ui/scripts/accounts.js index 529e5e3..86efb3e 100644 --- a/ui/scripts/accounts.js +++ b/ui/scripts/accounts.js @@ -1476,6 +1476,14 @@ form: { title: 'label.action.change.password', fields: { + currentPassword: { + label: 'label.current.password', + isPassword: true, + validation: { + required: !(isAdmin() || isDomainAdmin()) + }, + id: 'currentPassword' + }, newPassword: { label: 'label.new.password', isPassword: true, @@ -1496,13 +1504,13 @@ }, after: function(args) { start(); - + var currentPassword = args.data.currentPassword; var password = args.data.newPassword; - $.ajax({ url: createURL('updateUser'), data: { id: context.users[0].id, + currentPassword: currentPassword, password: password }, type: "POST", @@ -1515,6 +1523,9 @@ }); } }); + if(isAdmin() || isDomainAdmin()){ + $('div[rel=currentPassword]').hide(); + } } else { cloudStack.dialog.notice({ message: _l('error.could.not.change.your.password.because.non.native.user') }); } -- To stop receiving notification emails like this one, please contact raf...@apache.org.