You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by am...@apache.org on 2017/08/21 11:38:31 UTC
ambari git commit: AMBARI-21680. Prevent users from authenticating if
they exceed a configured number of login failures (amagyar)
Repository: ambari
Updated Branches:
refs/heads/branch-feature-AMBARI-20859 317905e40 -> 3cefb74cd
AMBARI-21680. Prevent users from authenticating if they exceed a configured number of login failures (amagyar)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/3cefb74c
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/3cefb74c
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/3cefb74c
Branch: refs/heads/branch-feature-AMBARI-20859
Commit: 3cefb74cdae3a836ee1896a30dca713e44b95f98
Parents: 317905e
Author: Attila Magyar <am...@hortonworks.com>
Authored: Thu Aug 10 11:37:00 2017 +0200
Committer: Attila Magyar <am...@hortonworks.com>
Committed: Mon Aug 21 13:37:28 2017 +0200
----------------------------------------------------------------------
.../scripts/controllers/users/UsersShowCtrl.js | 6 ++++-
.../ui/admin-web/app/scripts/i18n.config.js | 2 ++
.../ui/admin-web/app/scripts/services/User.js | 9 +++++++
.../ui/admin-web/app/views/users/show.html | 13 ++++++++++
ambari-server/docs/configuration/index.md | 1 +
.../server/configuration/Configuration.java | 13 ++++++++++
.../ambari/server/controller/AmbariServer.java | 5 ++--
.../ambari/server/controller/UserRequest.java | 10 ++++++++
.../internal/UserResourceProvider.java | 11 ++++++++
.../AmbariAuthenticationEventHandlerImpl.java | 2 +-
.../TooManyLoginFailuresException.java | 27 ++++++++++++++++++++
.../authorization/AmbariLocalUserProvider.java | 12 +++++++--
.../AmbariLocalUserProviderTest.java | 18 +++++++++++++
13 files changed, 123 insertions(+), 6 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/3cefb74c/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersShowCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersShowCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersShowCtrl.js
index 200872e..014703d 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersShowCtrl.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersShowCtrl.js
@@ -243,7 +243,11 @@ angular.module('ambariAdminConsole')
});
});
};
-
+ $scope.resetLoginFailures = function() {
+ User.resetLoginFailures($scope.user.user_name).then(function() {
+ $scope.user.consecutive_failures = 0;
+ });
+ };
// Load privileges
function loadPrivileges(){
User.getPrivileges($routeParams.id).then(function(data) {
http://git-wip-us.apache.org/repos/asf/ambari/blob/3cefb74c/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
index 43b32da..f83a8b3 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
@@ -302,6 +302,8 @@ angular.module('ambariAdminConsole')
'users.inactive': 'Inactive',
'users.status': 'Status',
'users.password': 'Password',
+ 'users.loginFailures': 'Login failures',
+ 'users.resetLoginFailures': 'Reset',
'users.passwordConfirmation': 'Password сonfirmation',
'users.userIsAdmin': 'This user is an Ambari Admin and has all privileges.',
'users.showAll': 'Show all users',
http://git-wip-us.apache.org/repos/asf/ambari/blob/3cefb74c/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js
index ac50653..1393362 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js
@@ -85,6 +85,15 @@ angular.module('ambariAdminConsole')
}
});
},
+ resetLoginFailures: function(userId) {
+ return $http({
+ method: 'PUT',
+ url: Settings.baseUrl + '/users/' + userId,
+ data: {
+ 'Users/consecutive_failures': 0
+ }
+ });
+ },
/**
* Generate user info to display by response data from API.
* Generally this is a single point to manage all required and useful data
http://git-wip-us.apache.org/repos/asf/ambari/blob/3cefb74c/ambari-admin/src/main/resources/ui/admin-web/app/views/users/show.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/users/show.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/users/show.html
index f965c5d..de4f14a 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/users/show.html
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/users/show.html
@@ -60,6 +60,19 @@
</div>
</div>
<div class="form-group">
+ <label for="" class="col-sm-2 control-label">{{'users.loginFailures' | translate}}</label>
+ <label class="col-sm-10 form-control-static">{{user.consecutive_failures}}</label>
+ </div>
+ <div class="form-group">
+ <div for="" class="col-sm-2"></div>
+ <div class="col-sm-10">
+ <div ng-switch="user.user_type != 'LOCAL'">
+ <button class="btn deleteuser-btn disabled btn-default" ng-switch-when="true" tooltip="{{'users.resetLoginFailures' | translate}}">{{'users.resetLoginFailures' | translate}}</button>
+ <a href ng-click="resetLoginFailures()" ng-switch-when="false" class="btn btn-default changepassword">{{'users.resetLoginFailures' | translate}}</a>
+ </div>
+ </div>
+ </div>
+ <div class="form-group">
<label for="groups" class="col-sm-2 control-label">{{getUserMembership(user.user_type)}}</label>
<div class="col-sm-10">
<editable-list items-source="editingGroupsList" resource-type="Group" editable="user.user_type == 'LOCAL'"></editable-list>
http://git-wip-us.apache.org/repos/asf/ambari/blob/3cefb74c/ambari-server/docs/configuration/index.md
----------------------------------------------------------------------
diff --git a/ambari-server/docs/configuration/index.md b/ambari-server/docs/configuration/index.md
index 9dbe9c4..395687d 100644
--- a/ambari-server/docs/configuration/index.md
+++ b/ambari-server/docs/configuration/index.md
@@ -109,6 +109,7 @@ The following are the properties which can be used to configure Ambari.
| authentication.ldap.userSearchFilter | A filter used to lookup a user in LDAP based on the Ambari user name<br/><br/>The following are examples of valid values:<ul><li>`(&({usernameAttribute}={0})(objectClass={userObjectClass}))`</ul> |`(&({usernameAttribute}={0})(objectClass={userObjectClass}))` |
| authentication.ldap.username.forceLowercase | Declares whether to force the ldap user name to be lowercase or leave as-is. This is useful when local user names are expected to be lowercase but the LDAP user names are not. |`false` |
| authentication.ldap.usernameAttribute | The attribute used for determining the user name, such as `uid`. |`uid` |
+| authentication.local.max.failures | The maximum number of authentication attempts permitted to a local user. Once the number of failures reaches this limit the user will be locked out. 0 indicates unlimited failures. |`10` |
| authorization.ldap.adminGroupMappingRules | A comma-separate list of groups which would give a user administrative access to Ambari when syncing from LDAP. This is only used when `authorization.ldap.groupSearchFilter` is blank.<br/><br/>The following are examples of valid values:<ul><li>`administrators`<li>`Hadoop Admins,Hadoop Admins.*,DC Admins,.*Hadoop Operators`</ul> |`Ambari Administrators` |
| authorization.ldap.groupSearchFilter | The DN to use when searching for LDAP groups. | |
| auto.group.creation | The auto group creation by Ambari |`false` |
http://git-wip-us.apache.org/repos/asf/ambari/blob/3cefb74c/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
index 4f787c6..fa6e9f9 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
@@ -2765,6 +2765,15 @@ public class Configuration {
"notification.dispatch.alert.script.directory",AmbariPath.getPath("/var/lib/ambari-server/resources/scripts"));
+ /**
+ * The maximum number of authentication attempts permitted to a local user. Once the number of failures reaches this limit the user will be locked out. 0 indicates unlimited failures
+ */
+ @Markdown(
+ description = "The maximum number of authentication attempts permitted to a local user. Once the number of failures reaches this limit the user will be locked out. 0 indicates unlimited failures.")
+ public static final ConfigurationProperty<Integer> MAX_LOCAL_AUTHENTICATION_FAILURES = new ConfigurationProperty<>(
+ "authentication.local.max.failures", 10);
+
+
private static final Logger LOG = LoggerFactory.getLogger(
Configuration.class);
@@ -6189,4 +6198,8 @@ public class Configuration {
public String getAutoGroupCreation() {
return getProperty(AUTO_GROUP_CREATION);
}
+
+ public int getMaxAuthenticationFailures() {
+ return Integer.parseInt(getProperty(MAX_LOCAL_AUTHENTICATION_FAILURES));
+ }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/3cefb74c/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
index b52e2b1..21ab757 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
@@ -339,8 +339,9 @@ public class AmbariServer {
injector.getInstance(PermissionHelper.class));
factory.registerSingleton("ambariLdapAuthenticationProvider",
injector.getInstance(AmbariLdapAuthenticationProvider.class));
- factory.registerSingleton("ambariLocalAuthenticationProvider",
- injector.getInstance(AmbariLocalUserProvider.class));
+ AmbariLocalUserProvider ambariLocalUserProvider = injector.getInstance(AmbariLocalUserProvider.class);
+ ambariLocalUserProvider.setMaxConsecutiveFailures(configs.getMaxAuthenticationFailures());
+ factory.registerSingleton("ambariLocalAuthenticationProvider", ambariLocalUserProvider);
factory.registerSingleton("ambariLdapDataPopulator",
injector.getInstance(AmbariLdapDataPopulator.class));
factory.registerSingleton("ambariUserAuthorizationFilter",
http://git-wip-us.apache.org/repos/asf/ambari/blob/3cefb74c/ambari-server/src/main/java/org/apache/ambari/server/controller/UserRequest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/UserRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/UserRequest.java
index d0836a9..2f155b6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/UserRequest.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/UserRequest.java
@@ -35,6 +35,7 @@ public class UserRequest {
private String displayName;
private String localUserName;
+ private Integer consecutiveFailures;
public UserRequest(String name) {
this.userName = name;
@@ -99,6 +100,15 @@ public class UserRequest {
this.localUserName = localUserName;
}
+ @ApiModelProperty(name = UserResourceProvider.CONSECUTIVE_FAILURES_PROPERTY_ID)
+ public Integer getConsecutiveFailures() {
+ return consecutiveFailures;
+ }
+
+ public void setConsecutiveFailures(Integer consecutiveFailures) {
+ this.consecutiveFailures = consecutiveFailures;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
http://git-wip-us.apache.org/repos/asf/ambari/blob/3cefb74c/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserResourceProvider.java
index a2d9917..99f88ca 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserResourceProvider.java
@@ -333,6 +333,10 @@ public class UserResourceProvider extends AbstractControllerResourceProvider imp
request.setAdmin(Boolean.valueOf(properties.get(USER_ADMIN_PROPERTY_ID).toString()));
}
+ if (null != properties.get(USER_CONSECUTIVE_FAILURES_PROPERTY_ID)) {
+ request.setConsecutiveFailures(Integer.parseInt(properties.get(USER_CONSECUTIVE_FAILURES_PROPERTY_ID).toString()));
+ }
+
return request;
}
@@ -476,6 +480,13 @@ public class UserResourceProvider extends AbstractControllerResourceProvider imp
if (request.getPassword() != null) {
addOrUpdateLocalAuthenticationSource(asUserAdministrator, userEntity, request.getPassword(), request.getOldPassword());
}
+
+ if (request.getConsecutiveFailures() != null) {
+ if (!asUserAdministrator) {
+ throw new AuthorizationException("The authenticated user is not authorized to update the requested resource property");
+ }
+ users.safelyUpdateUserEntity(userEntity, user -> user.setConsecutiveFailures(request.getConsecutiveFailures()));
+ }
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/3cefb74c/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandlerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandlerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandlerImpl.java
index 3a5a66b..2a89437 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandlerImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationEventHandlerImpl.java
@@ -125,7 +125,7 @@ public class AmbariAuthenticationEventHandlerImpl implements AmbariAuthenticatio
AuditEvent loginFailedAuditEvent = LoginAuditEvent.builder()
.withRemoteIp(RequestUtils.getRemoteAddress(servletRequest))
.withTimestamp(System.currentTimeMillis())
- .withReasonOfFailure("Invalid username/password combination")
+ .withReasonOfFailure(message)
.withConsecutiveFailures(consecutiveFailures)
.withUserName(username)
.build();
http://git-wip-us.apache.org/repos/asf/ambari/blob/3cefb74c/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/TooManyLoginFailuresException.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/TooManyLoginFailuresException.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/TooManyLoginFailuresException.java
new file mode 100644
index 0000000..b172079
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/TooManyLoginFailuresException.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.security.authentication;
+
+/**
+ * Thrown when the consecutive authentication failures exceed the limit
+ */
+public class TooManyLoginFailuresException extends AmbariAuthenticationException {
+ public TooManyLoginFailuresException(String username) {
+ super(username, "Too many authentication failures");
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/3cefb74c/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProvider.java
index 2c8bf12..2a2e397 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProvider.java
@@ -23,6 +23,7 @@ import org.apache.ambari.server.orm.dao.UserDAO;
import org.apache.ambari.server.orm.entities.UserAuthenticationEntity;
import org.apache.ambari.server.orm.entities.UserEntity;
import org.apache.ambari.server.security.authentication.InvalidUsernamePasswordCombinationException;
+import org.apache.ambari.server.security.authentication.TooManyLoginFailuresException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -36,11 +37,10 @@ import com.google.inject.Inject;
public class AmbariLocalUserProvider extends AbstractUserDetailsAuthenticationProvider {
private static final Logger LOG = LoggerFactory.getLogger(AmbariLocalUserProvider.class);
-
private UserDAO userDAO;
private Users users;
private PasswordEncoder passwordEncoder;
-
+ private int maxConsecutiveFailures = 0;
@Inject
public AmbariLocalUserProvider(UserDAO userDAO, Users users, PasswordEncoder passwordEncoder) {
@@ -75,6 +75,10 @@ public class AmbariLocalUserProvider extends AbstractUserDetailsAuthenticationPr
throw new InvalidUsernamePasswordCombinationException(userName);
}
+ if (maxConsecutiveFailures > 0 && userEntity.getConsecutiveFailures() >= maxConsecutiveFailures) {
+ throw new TooManyLoginFailuresException(userName);
+ }
+
if (authentication.getCredentials() == null) {
LOG.debug("Authentication failed: no credentials provided");
throw new InvalidUsernamePasswordCombinationException(userName);
@@ -111,4 +115,8 @@ public class AmbariLocalUserProvider extends AbstractUserDetailsAuthenticationPr
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
+
+ public void setMaxConsecutiveFailures(int maxConsecutiveFailures) {
+ this.maxConsecutiveFailures = maxConsecutiveFailures;
+ }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/3cefb74c/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProviderTest.java
index 133fc9f..fb4ebf9 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProviderTest.java
@@ -36,6 +36,7 @@ import org.apache.ambari.server.orm.entities.PrincipalEntity;
import org.apache.ambari.server.orm.entities.UserAuthenticationEntity;
import org.apache.ambari.server.orm.entities.UserEntity;
import org.apache.ambari.server.security.authentication.InvalidUsernamePasswordCombinationException;
+import org.apache.ambari.server.security.authentication.TooManyLoginFailuresException;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -153,7 +154,24 @@ public class AmbariLocalUserProviderTest {
ambariLocalUserProvider.authenticate(authentication);
}
+ @Test(expected = TooManyLoginFailuresException.class)
+ public void testUserIsLockedOutAfterConsecutiveFailures() {
+ Users users = createMock(Users.class);
+ UserDAO userDAO = createMock(UserDAO.class);
+ Authentication authentication = createMock(Authentication.class);
+ UserEntity userEntity = combineUserEntity();
+ userEntity.setConsecutiveFailures(3);
+ expect(authentication.getName()).andReturn(TEST_USER_NAME).anyTimes();
+ expect(authentication.getCredentials()).andReturn(TEST_USER_PASS).anyTimes();
+ expect(userDAO.findUserByName(TEST_USER_NAME)).andReturn(userEntity).anyTimes();
+ expect(users.getUserAuthorities(userEntity)).andReturn(null);
+
+ replay(users, userDAO, authentication);
+ AmbariLocalUserProvider ambariLocalUserProvider = new AmbariLocalUserProvider(userDAO, users, passwordEncoder);
+ ambariLocalUserProvider.setMaxConsecutiveFailures(3);
+ ambariLocalUserProvider.authenticate(authentication);
+ }
private UserEntity combineUserEntity() {
PrincipalEntity principalEntity = new PrincipalEntity();