bug CS-15278: Added global setting login.attempts.allowed which defines the maximum incorrect password attempts allowed. Also after the maximum attempts are reached the user account is disabled
Project: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/commit/a56631bc Tree: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/tree/a56631bc Diff: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/diff/a56631bc Branch: refs/heads/javelin Commit: a56631bc66713faaa99759eb374e11afd6f533d3 Parents: 41f6127 Author: Saksham Srivastava <[email protected]> Authored: Wed Sep 12 19:16:16 2012 +0530 Committer: Nitin Mehta <[email protected]> Committed: Wed Sep 12 19:35:24 2012 +0530 ---------------------------------------------------------------------- core/src/com/cloud/user/UserAccountVO.java | 13 +++- server/src/com/cloud/configuration/Config.java | 2 +- .../configuration/ConfigurationManagerImpl.java | 1 + server/src/com/cloud/user/AccountManagerImpl.java | 62 ++++++++++++++- setup/db/create-schema.sql | 1 + setup/db/db/schema-302to40.sql | 3 + 6 files changed, 78 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/a56631bc/core/src/com/cloud/user/UserAccountVO.java ---------------------------------------------------------------------- diff --git a/core/src/com/cloud/user/UserAccountVO.java b/core/src/com/cloud/user/UserAccountVO.java index 5e7c018..1236061 100644 --- a/core/src/com/cloud/user/UserAccountVO.java +++ b/core/src/com/cloud/user/UserAccountVO.java @@ -83,6 +83,9 @@ public class UserAccountVO implements UserAccount { @Column(name="is_registered") boolean registered; + @Column (name="incorrect_login_attempts") + int loginAttempts; + @Column(name="account_name", table="account", insertable=false, updatable=false) private String accountName = null; @@ -269,4 +272,12 @@ public class UserAccountVO implements UserAccount { public void setRegistered(boolean registered) { this.registered = registered; } -} \ No newline at end of file + + public void setLoginAttempts(int loginAttempts) { + this.loginAttempts = loginAttempts; + } + + public int getLoginAttempts() { + return loginAttempts; + } +} http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/a56631bc/server/src/com/cloud/configuration/Config.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 8554836..764e5ee 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -229,7 +229,7 @@ public enum Config { EnableEC2API("Advanced", ManagementServer.class, Boolean.class, "enable.ec2.api", "false", "enable EC2 API on CloudStack", null), EnableS3API("Advanced", ManagementServer.class, Boolean.class, "enable.s3.api", "false", "enable Amazon S3 API on CloudStack", null), RecreateSystemVmEnabled("Advanced", ManagementServer.class, Boolean.class, "recreate.systemvm.enabled", "false", "If true, will recreate system vm root disk whenever starting system vm", "true,false"), - + IncorrectLoginAttemptsAllowed("Advanced", ManagementServer.class, Integer.class, "incorrect.login.attempts.allowed", "5", "Incorrect login attempts allowed before the user is disabled", null), // Ovm OvmPublicNetwork("Hidden", ManagementServer.class, String.class, "ovm.public.network.device", null, "Specify the public bridge on host for public network", null), OvmPrivateNetwork("Hidden", ManagementServer.class, String.class, "ovm.private.network.device", null, "Specify the private bridge on host for private network", null), http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/a56631bc/server/src/com/cloud/configuration/ConfigurationManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index f8deb15..ef940e8 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -274,6 +274,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura configValuesForValidation.add("storage.cleanup.interval"); configValuesForValidation.add("wait"); configValuesForValidation.add("xen.heartbeat.interval"); + configValuesForValidation.add("incorrect.login.attempts.allowed"); } @Override http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/a56631bc/server/src/com/cloud/user/AccountManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 38153f3..fa9fafb 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -224,6 +224,8 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("AccountChecker")); + int _allowedLoginAttempts; + UserVO _systemUser; AccountVO _systemAccount; @Inject(adapter = SecurityChecker.class) @@ -248,6 +250,9 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag ConfigurationDao configDao = locator.getDao(ConfigurationDao.class); Map<String, String> configs = configDao.getConfiguration(params); + String loginAttempts = configs.get(Config.IncorrectLoginAttemptsAllowed.key()); + _allowedLoginAttempts = NumbersUtil.parseInt(loginAttempts, 5); + String value = configs.get(Config.AccountCleanupInterval.key()); _cleanupInterval = NumbersUtil.parseInt(value, 60 * 60 * 24); // 1 day. @@ -302,6 +307,13 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag return (accountType == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN); } + public boolean isInternalAccount(short accountType) { + if (isRootAdmin(accountType) || (accountType == Account.ACCOUNT_ID_SYSTEM)) { + return true; + } + return false; + } + @Override public void checkAccess(Account caller, Domain domain) throws PermissionDeniedException { for (SecurityChecker checker : _securityCheckers) { @@ -420,6 +432,25 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag } + @DB + public void updateLoginAttempts(Long id, int attempts, boolean toDisable) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + try { + UserAccountVO user = null; + user = _userAccountDao.lockRow(id, true); + user.setLoginAttempts(attempts); + if(toDisable) { + user.setState(State.disabled.toString()); + } + _userAccountDao.update(id, user); + txn.commit(); + } catch (Exception e) { + s_logger.error("Failed to update login attempts for user with id " + id ); + } + txn.close(); + } + private boolean doSetUserStatus(long userId, State state) { UserVO userForUpdate = _userDao.createForUpdate(); userForUpdate.setState(state); @@ -732,7 +763,7 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag if (domainId == null) { domainId = DomainVO.ROOT_DOMAIN; } - + if (userName.isEmpty()) { throw new InvalidParameterValueException("Username is empty"); } @@ -740,7 +771,7 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag if (firstName.isEmpty()) { throw new InvalidParameterValueException("Firstname is empty"); } - + if (lastName.isEmpty()) { throw new InvalidParameterValueException("Lastname is empty"); } @@ -1002,6 +1033,8 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag txn.commit(); if (success) { + // whenever the user is successfully enabled, reset the login attempts to zero + updateLoginAttempts(userId, 0, false); return _userAccountDao.findById(userId); } else { throw new CloudRuntimeException("Unable to enable user " + userId); @@ -1818,11 +1851,36 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag throw new CloudAuthenticationException("User " + username + " in domain " + domainName + " is disabled/locked (or account is disabled/locked)"); // return null; } + // Whenever the user is able to log in successfully, reset the login attempts to zero + if(!isInternalAccount(userAccount.getType())) + updateLoginAttempts(userAccount.getId(), 0, false); + return userAccount; } else { if (s_logger.isDebugEnabled()) { s_logger.debug("Unable to authenticate user with username " + username + " in domain " + domainId); } + + UserAccount userAccount = _userAccountDao.getUserAccount(username, domainId); + UserAccountVO user = _userAccountDao.findById(userAccount.getId()); + if (user != null) { + if ((user.getState().toString()).equals("enabled")) { + if (!isInternalAccount(user.getType())) { + //Internal accounts are not disabled + int attemptsMade = user.getLoginAttempts() + 1; + if (attemptsMade < _allowedLoginAttempts) { + updateLoginAttempts(userAccount.getId(), attemptsMade, false); + s_logger.warn("Login attempt failed. You have " + ( _allowedLoginAttempts - attemptsMade ) + " attempt(s) remaining"); + } else { + updateLoginAttempts(userAccount.getId(), _allowedLoginAttempts, true); + s_logger.warn("User " + user.getUsername() + " has been disabled due to multiple failed login attempts." + + " Please contact admin."); + } + } + } else { + s_logger.info("User " + user.getUsername() + " is disabled/locked"); + } + } return null; } } http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/a56631bc/setup/db/create-schema.sql ---------------------------------------------------------------------- diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index bb6dc5d..5b6dc04 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -890,6 +890,7 @@ CREATE TABLE `cloud`.`user` ( `timezone` varchar(30) default NULL, `registration_token` varchar(255) default NULL, `is_registered` tinyint NOT NULL DEFAULT 0 COMMENT '1: yes, 0: no', + `incorrect_login_attempts` integer unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`), INDEX `i_user__removed`(`removed`), INDEX `i_user__secret_key_removed`(`secret_key`, `removed`), http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/a56631bc/setup/db/db/schema-302to40.sql ---------------------------------------------------------------------- diff --git a/setup/db/db/schema-302to40.sql b/setup/db/db/schema-302to40.sql index d70697b..0916156 100644 --- a/setup/db/db/schema-302to40.sql +++ b/setup/db/db/schema-302to40.sql @@ -472,3 +472,6 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'manage INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'site2site.vpn.customergateway.subnets.limit', '10', 'The maximum number of subnets per customer gateway'); INSERT IGNORE INTO `cloud`.`guest_os_category` VALUES ('11','None',NULL); +ALTER TABLE `cloud`.`user` ADD COLUMN `incorrect_login_attempts` integer unsigned NOT NULL DEFAULT '0'; +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'incorrect.login.attempts.allowed', '5', 'Incorrect login attempts allowed before the user is disabled'); +
