You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by rl...@apache.org on 2017/10/04 15:03:19 UTC
ambari git commit: AMBARI-21220. Update Local Authentication process
to work with improved user management facility (rlevas)
Repository: ambari
Updated Branches:
refs/heads/branch-feature-AMBARI-20859 9eb26a0d8 -> e1699b092
AMBARI-21220. Update Local Authentication process to work with improved user management facility (rlevas)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/e1699b09
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/e1699b09
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/e1699b09
Branch: refs/heads/branch-feature-AMBARI-20859
Commit: e1699b09265e54392ec246a17deec5043f76f54a
Parents: 9eb26a0
Author: Robert Levas <rl...@hortonworks.com>
Authored: Wed Oct 4 11:03:06 2017 -0400
Committer: Robert Levas <rl...@hortonworks.com>
Committed: Wed Oct 4 11:03:06 2017 -0400
----------------------------------------------------------------------
ambari-server/docs/configuration/index.md | 1 +
.../server/configuration/Configuration.java | 13 +-
.../ambari/server/controller/AmbariServer.java | 7 +-
.../AccountDisabledException.java | 27 +++
.../AmbariAuthenticationEventHandlerImpl.java | 11 +-
.../AmbariAuthenticationProvider.java | 99 +++++++++
.../AmbariLocalAuthenticationProvider.java | 111 ++++++++++
.../authorization/AmbariLocalUserProvider.java | 122 -----------
.../server/security/authorization/Users.java | 2 +-
.../AbstractAuthenticationProviderTest.java | 213 +++++++++++++++++++
.../AmbariLocalAuthenticationProviderTest.java | 91 ++++++++
...ariAuthorizationProviderDisableUserTest.java | 108 ----------
.../AmbariLocalUserProviderTest.java | 190 -----------------
13 files changed, 566 insertions(+), 429 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/e1699b09/ambari-server/docs/configuration/index.md
----------------------------------------------------------------------
diff --git a/ambari-server/docs/configuration/index.md b/ambari-server/docs/configuration/index.md
index 395687d..73d6fd0 100644
--- a/ambari-server/docs/configuration/index.md
+++ b/ambari-server/docs/configuration/index.md
@@ -110,6 +110,7 @@ The following are the properties which can be used to configure Ambari.
| 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` |
+| authentication.local.show.locked.account.messages | Show or hide whether the user account is disabled or locked out, if relevant, when an authentication attempt fails. |`false` |
| 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/e1699b09/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 3099bc0..62e8b86 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
@@ -2768,11 +2768,16 @@ public class Configuration {
/**
* 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.")
+ @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);
+ /**
+ * A flag to determine whether locked out messages are to be shown to users, if relevant, when authenticating into Ambari
+ */
+ @Markdown(description = "Show or hide whether the user account is disabled or locked out, if relevant, when an authentication attempt fails.")
+ public static final ConfigurationProperty<String> SHOW_LOCKED_OUT_USER_MESSAGE = new ConfigurationProperty<>(
+ "authentication.local.show.locked.account.messages", "false");
private static final Logger LOG = LoggerFactory.getLogger(
Configuration.class);
@@ -6206,4 +6211,8 @@ public class Configuration {
public int getMaxAuthenticationFailures() {
return Integer.parseInt(getProperty(MAX_LOCAL_AUTHENTICATION_FAILURES));
}
+
+ public boolean showLockedOutUserMessage() {
+ return Boolean.parseBoolean(getProperty(SHOW_LOCKED_OUT_USER_MESSAGE));
+ }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/e1699b09/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 21ab757..0d24ef2 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
@@ -100,8 +100,8 @@ import org.apache.ambari.server.security.AmbariViewsSecurityHeaderFilter;
import org.apache.ambari.server.security.CertificateManager;
import org.apache.ambari.server.security.SecurityFilter;
import org.apache.ambari.server.security.authentication.AmbariAuthenticationEventHandlerImpl;
+import org.apache.ambari.server.security.authentication.AmbariLocalAuthenticationProvider;
import org.apache.ambari.server.security.authorization.AmbariLdapAuthenticationProvider;
-import org.apache.ambari.server.security.authorization.AmbariLocalUserProvider;
import org.apache.ambari.server.security.authorization.AmbariPamAuthenticationProvider;
import org.apache.ambari.server.security.authorization.AmbariUserAuthorizationFilter;
import org.apache.ambari.server.security.authorization.PermissionHelper;
@@ -339,9 +339,8 @@ public class AmbariServer {
injector.getInstance(PermissionHelper.class));
factory.registerSingleton("ambariLdapAuthenticationProvider",
injector.getInstance(AmbariLdapAuthenticationProvider.class));
- AmbariLocalUserProvider ambariLocalUserProvider = injector.getInstance(AmbariLocalUserProvider.class);
- ambariLocalUserProvider.setMaxConsecutiveFailures(configs.getMaxAuthenticationFailures());
- factory.registerSingleton("ambariLocalAuthenticationProvider", ambariLocalUserProvider);
+ factory.registerSingleton("ambariLocalAuthenticationProvider",
+ injector.getInstance(AmbariLocalAuthenticationProvider.class));
factory.registerSingleton("ambariLdapDataPopulator",
injector.getInstance(AmbariLdapDataPopulator.class));
factory.registerSingleton("ambariUserAuthorizationFilter",
http://git-wip-us.apache.org/repos/asf/ambari/blob/e1699b09/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AccountDisabledException.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AccountDisabledException.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AccountDisabledException.java
new file mode 100644
index 0000000..4a88f46
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AccountDisabledException.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 account has been flagged as inactive
+ */
+public class AccountDisabledException extends AmbariAuthenticationException {
+ public AccountDisabledException(String username) {
+ super(username, "The account is disabled");
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/e1699b09/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 2a89437..4cfce2a 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
@@ -106,10 +106,17 @@ public class AmbariAuthenticationEventHandlerImpl implements AmbariAuthenticatio
message = cause.getLocalizedMessage();
}
- // Increment the user's consecutive authentication failure count.
if (!StringUtils.isEmpty(username)) {
+ // Increment the user's consecutive authentication failure count.
consecutiveFailures = users.incrementConsecutiveAuthenticationFailures(username);
- logMessage = String.format("Failed to authenticate %s (attempt #%d): %s", username, consecutiveFailures, message);
+
+ // If consecutiveFailures is NULL, then no user entry was found for the specified username.
+ if(consecutiveFailures == null) {
+ logMessage = String.format("Failed to authenticate %s: The user does not exist in the Ambari database", username);
+ }
+ else {
+ logMessage = String.format("Failed to authenticate %s (attempt #%d): %s", username, consecutiveFailures, message);
+ }
} else {
logMessage = String.format("Failed to authenticate an unknown user: %s", message);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/e1699b09/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationProvider.java
new file mode 100644
index 0000000..3d20cb9
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariAuthenticationProvider.java
@@ -0,0 +1,99 @@
+/*
+ * 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;
+
+import java.util.Collection;
+
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.orm.entities.UserAuthenticationEntity;
+import org.apache.ambari.server.orm.entities.UserEntity;
+import org.apache.ambari.server.security.authorization.UserAuthenticationType;
+import org.apache.ambari.server.security.authorization.Users;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AuthenticationProvider;
+
+/**
+ * AmbariAuthenticationProvider is an abstract class to be extended by Ambari authentication providers.
+ * <p>
+ * This class contains common methods that may be used by authentication providers.
+ */
+abstract class AmbariAuthenticationProvider implements AuthenticationProvider {
+ private static final Logger LOG = LoggerFactory.getLogger(AmbariAuthenticationProvider.class);
+
+ private Users users;
+ private Configuration configuration;
+
+ AmbariAuthenticationProvider(Users users, Configuration configuration) {
+ this.users = users;
+ this.configuration = configuration;
+ }
+
+ /**
+ * Gets the {@link UserEntity} for the user with the specified username.
+ * <p>
+ * The entity is validated such that the account is allowed to log in before returning. For example,
+ * if the account is not acitve, no user may not login as that account.
+ *
+ * @param userName
+ * @return
+ */
+ UserEntity getUserEntity(String userName) {
+ LOG.debug("Loading user by name: {}", userName);
+ UserEntity userEntity = users.getUserEntity(userName);
+
+ if (userEntity == null) {
+ LOG.info("User not found: {}", userName);
+ throw new InvalidUsernamePasswordCombinationException(userName);
+ }
+
+ if (!userEntity.getActive()) {
+ LOG.info("User account is disabled: {}", userName);
+ if (configuration.showLockedOutUserMessage()) {
+ throw new AccountDisabledException(userName);
+ } else {
+ throw new InvalidUsernamePasswordCombinationException(userName);
+ }
+ }
+
+ return userEntity;
+ }
+
+ /**
+ * Finds the specific {@link UserAuthenticationEntity} from the collection of authentication methods
+ * available to the specified {@link UserEntity}.
+ *
+ * @param userEntity a {@link UserEntity}
+ * @param type the {@link UserAuthenticationType} to retrieve
+ * @return a {@link UserAuthenticationEntity} if found; otherwise null
+ */
+ UserAuthenticationEntity getAuthenticationEntity(UserEntity userEntity, UserAuthenticationType type) {
+ Collection<UserAuthenticationEntity> authenticationEntities = (userEntity == null) ? null : userEntity.getAuthenticationEntities();
+ if (authenticationEntities != null) {
+ for (UserAuthenticationEntity authenticationEntity : authenticationEntities) {
+ if (authenticationEntity.getAuthenticationType() == type) {
+ return authenticationEntity;
+ }
+ }
+ }
+
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/e1699b09/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProvider.java
new file mode 100644
index 0000000..dcdf471
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProvider.java
@@ -0,0 +1,111 @@
+/*
+ * 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;
+
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.orm.entities.UserAuthenticationEntity;
+import org.apache.ambari.server.orm.entities.UserEntity;
+import org.apache.ambari.server.security.authorization.AmbariUserAuthentication;
+import org.apache.ambari.server.security.authorization.User;
+import org.apache.ambari.server.security.authorization.UserAuthenticationType;
+import org.apache.ambari.server.security.authorization.Users;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import com.google.inject.Inject;
+
+/**
+ * AmbariLocalAuthenticationProvider is an {@link org.springframework.security.authentication.AuthenticationProvider}
+ * implementation used to authenticate users using username and password details from the local Ambari database.
+ * <p>
+ * Users will fail to authenticate, even if they supply the correct credentials if the account is locked out
+ * by being disabled or locked due to too many consecutive failure.
+ */
+public class AmbariLocalAuthenticationProvider extends AmbariAuthenticationProvider {
+ private static final Logger LOG = LoggerFactory.getLogger(AmbariLocalAuthenticationProvider.class);
+
+ private Users users;
+ private PasswordEncoder passwordEncoder;
+ private Configuration configuration;
+
+ @Inject
+ public AmbariLocalAuthenticationProvider(Users users, PasswordEncoder passwordEncoder, Configuration configuration) {
+ super(users, configuration);
+ this.users = users;
+ this.passwordEncoder = passwordEncoder;
+ this.configuration = configuration;
+ }
+
+ @Override
+ public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+ String userName = authentication.getName().trim();
+
+ UserEntity userEntity = getUserEntity(userName);
+
+ if (userEntity == null) {
+ LOG.info("User not found: {}", userName);
+ throw new InvalidUsernamePasswordCombinationException(userName);
+ }
+
+ int maxConsecutiveFailures = configuration.getMaxAuthenticationFailures();
+ if (maxConsecutiveFailures > 0 && userEntity.getConsecutiveFailures() >= maxConsecutiveFailures) {
+ LOG.info("User account is locked out due to too many authentication failures ({}/{}): {}",
+ userEntity.getConsecutiveFailures(), maxConsecutiveFailures, userName);
+ if (configuration.showLockedOutUserMessage()) {
+ throw new TooManyLoginFailuresException(userName);
+ } else {
+ throw new InvalidUsernamePasswordCombinationException(userName);
+ }
+ }
+
+
+ if (authentication.getCredentials() == null) {
+ LOG.info("Authentication failed: no credentials provided: {}", userName);
+ throw new InvalidUsernamePasswordCombinationException(userName);
+ }
+
+ UserAuthenticationEntity authenticationEntity = getAuthenticationEntity(userEntity, UserAuthenticationType.LOCAL);
+ if (authenticationEntity != null) {
+ String password = authenticationEntity.getAuthenticationKey();
+ String presentedPassword = authentication.getCredentials().toString();
+
+ if (passwordEncoder.matches(presentedPassword, password)) {
+ // The user was authenticated, return the authenticated user object
+ LOG.debug("Authentication succeeded - a matching username and password were found: {}", userName);
+
+ User user = new User(userEntity);
+ Authentication auth = new AmbariUserAuthentication(password, user, users.getUserAuthorities(userEntity));
+ auth.setAuthenticated(true);
+ return auth;
+ }
+ }
+
+ // The user was not authenticated, fail
+ LOG.debug("Authentication failed: password does not match stored value: {}", userName);
+ throw new InvalidUsernamePasswordCombinationException(userName);
+ }
+
+ @Override
+ public boolean supports(Class<?> authentication) {
+ return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/e1699b09/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
deleted file mode 100644
index 2a2e397..0000000
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProvider.java
+++ /dev/null
@@ -1,122 +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 org.apache.ambari.server.security.authorization;
-
-import java.util.List;
-
-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;
-import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.crypto.password.PasswordEncoder;
-
-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) {
- this.userDAO = userDAO;
- this.users = users;
- this.passwordEncoder = passwordEncoder;
- }
-
- @Override
- protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
- // do nothing
- }
-
- // TODO: ************
- // TODO: This is to be revisited for AMBARI-21220 (Update Local Authentication process to work with improved user management facility)
- // TODO: ************
- @Override
- public Authentication authenticate(Authentication authentication) throws AuthenticationException {
- String userName = authentication.getName().trim();
-
- LOG.info("Loading user by name: " + userName);
-
- UserEntity userEntity = userDAO.findUserByName(userName);
-
- if (userEntity == null) {
- LOG.info("user not found");
- throw new InvalidUsernamePasswordCombinationException(userName);
- }
-
- if (!userEntity.getActive()) {
- LOG.debug("User account is disabled");
- 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);
- }
-
- List<UserAuthenticationEntity> authenticationEntities = userEntity.getAuthenticationEntities();
- for (UserAuthenticationEntity authenticationEntity : authenticationEntities) {
- if (authenticationEntity.getAuthenticationType() == UserAuthenticationType.LOCAL) {
- // This should only get invoked once...
- String password = authenticationEntity.getAuthenticationKey();
- String presentedPassword = authentication.getCredentials().toString();
-
- if (passwordEncoder.matches(presentedPassword, password)) {
- // The user was authenticated, return the authenticated user object
- User user = new User(userEntity);
- Authentication auth = new AmbariUserAuthentication(password, user, users.getUserAuthorities(userEntity));
- auth.setAuthenticated(true);
- return auth;
- }
- }
- }
-
- // The user was not authenticated, fail
- LOG.debug("Authentication failed: password does not match stored value");
- throw new InvalidUsernamePasswordCombinationException(userName);
- }
-
- @Override
- protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
- return null;
- }
-
- @Override
- 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/e1699b09/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
index 2dedc9e..a5faea1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
@@ -1281,7 +1281,6 @@ public class Users {
}
/**
- * TODO: This is to be revisited for AMBARI-21220 (Update Local Authentication process to work with improved user management facility)
* Adds the ability for a user to authenticate using a password stored in Ambari's database
* <p>
* The supplied plaintext password will be encoded before storing.
@@ -1292,6 +1291,7 @@ public class Users {
*/
public void addLocalAuthentication(UserEntity userEntity, String password) throws AmbariException {
+ // Ensure the password meets configured minimal requirements, if any
validatePassword(password);
// Encode the password..
http://git-wip-us.apache.org/repos/asf/ambari/blob/e1699b09/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AbstractAuthenticationProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AbstractAuthenticationProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AbstractAuthenticationProviderTest.java
new file mode 100644
index 0000000..96b4883
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AbstractAuthenticationProviderTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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;
+
+import static org.easymock.EasyMock.anyString;
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import javax.persistence.EntityManager;
+
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.hooks.HookContextFactory;
+import org.apache.ambari.server.hooks.HookService;
+import org.apache.ambari.server.orm.DBAccessor;
+import org.apache.ambari.server.orm.entities.UserEntity;
+import org.apache.ambari.server.security.authorization.AmbariUserAuthentication;
+import org.apache.ambari.server.security.authorization.Users;
+import org.apache.ambari.server.state.stack.OsFamily;
+import org.easymock.EasyMockSupport;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+abstract class AbstractAuthenticationProviderTest extends EasyMockSupport {
+
+ static final String TEST_USER_NAME = "userName";
+
+ @Before
+ public void setUp() throws Exception {
+ SecurityContextHolder.getContext().setAuthentication(null);
+ }
+
+ @After
+ public void cleanUp() throws Exception {
+ SecurityContextHolder.getContext().setAuthentication(null);
+ }
+
+
+ @Test
+ public void testAuthenticationSuccess() {
+ Injector injector = getInjector();
+
+ UserEntity userEntity = getUserEntity(injector, TEST_USER_NAME, 9, true);
+
+ Users users = injector.getInstance(Users.class);
+ expect(users.getUserEntity(TEST_USER_NAME)).andReturn(userEntity).atLeastOnce();
+ expect(users.getUserAuthorities(userEntity)).andReturn(null).atLeastOnce();
+
+ Authentication authentication = getAuthentication(true, true);
+
+ replayAll();
+
+ AuthenticationProvider provider = getAuthenticationProvider(injector);
+ Authentication result = provider.authenticate(authentication);
+
+ verifyAll();
+
+ assertNotNull(result);
+ assertEquals(true, result.isAuthenticated());
+ assertTrue(result instanceof AmbariUserAuthentication);
+
+ validateAuthenticationResult((AmbariUserAuthentication) result);
+ }
+
+ @Test(expected = AmbariAuthenticationException.class)
+ public void testAuthenticationWithIncorrectUserName() {
+ Injector injector = getInjector();
+
+ Authentication authentication = getAuthentication(false, true);
+
+ Users users = injector.getInstance(Users.class);
+ expect(users.getUserEntity(anyString())).andReturn(null).atLeastOnce();
+
+ replayAll();
+
+ AuthenticationProvider provider = getAuthenticationProvider(injector);
+ provider.authenticate(authentication);
+ }
+
+
+ @Test(expected = AmbariAuthenticationException.class)
+ public void testAuthenticationWithoutCredentials() {
+ Injector injector = getInjector();
+
+ UserEntity userEntity = getUserEntity(injector, TEST_USER_NAME, 0, true);
+
+ Users users = injector.getInstance(Users.class);
+ expect(users.getUserEntity(TEST_USER_NAME)).andReturn(userEntity).atLeastOnce();
+ expect(users.getUserAuthorities(userEntity)).andReturn(null).atLeastOnce();
+
+ Authentication authentication = createMock(Authentication.class);
+ expect(authentication.getName()).andReturn(TEST_USER_NAME).atLeastOnce();
+ expect(authentication.getCredentials()).andReturn(null).atLeastOnce();
+
+ replayAll();
+
+ AuthenticationProvider provider = getAuthenticationProvider(injector);
+ provider.authenticate(authentication);
+ }
+
+
+ @Test(expected = AmbariAuthenticationException.class)
+ public void testAuthenticationWithIncorrectCredential() {
+ Injector injector = getInjector();
+
+ UserEntity userEntity = getUserEntity(injector, TEST_USER_NAME, 0, true);
+
+ Users users = injector.getInstance(Users.class);
+ expect(users.getUserEntity(TEST_USER_NAME)).andReturn(userEntity).atLeastOnce();
+ expect(users.getUserAuthorities(userEntity)).andReturn(null).atLeastOnce();
+
+ Authentication authentication = getAuthentication(true, false);
+
+ replayAll();
+
+ AuthenticationProvider provider = getAuthenticationProvider(injector);
+ provider.authenticate(authentication);
+ }
+
+ @Test(expected = TooManyLoginFailuresException.class)
+ public void testUserIsLockedOutAfterConsecutiveFailures() {
+ Injector injector = getInjector();
+
+ // Force the user to have more than 10 consecutive failures
+ UserEntity userEntity = getUserEntity(injector, TEST_USER_NAME, 11, true);
+
+ Users users = injector.getInstance(Users.class);
+ expect(users.getUserEntity(TEST_USER_NAME)).andReturn(userEntity).atLeastOnce();
+
+ Authentication authentication = getAuthentication(true, true);
+
+ replayAll();
+
+ AmbariLocalAuthenticationProvider ambariLocalAuthenticationProvider = injector.getInstance(AmbariLocalAuthenticationProvider.class);
+ ambariLocalAuthenticationProvider.authenticate(authentication);
+ }
+
+ @Test(expected = AccountDisabledException.class)
+ public void testUserIsInactive() {
+ Injector injector = getInjector();
+
+ // Force the user to be inactive
+ UserEntity userEntity = getUserEntity(injector, TEST_USER_NAME, 10, false);
+
+ Users users = injector.getInstance(Users.class);
+ expect(users.getUserEntity(TEST_USER_NAME)).andReturn(userEntity).atLeastOnce();
+
+ Authentication authentication = getAuthentication(true, true);
+
+ replayAll();
+
+ AmbariLocalAuthenticationProvider ambariLocalAuthenticationProvider = injector.getInstance(AmbariLocalAuthenticationProvider.class);
+ ambariLocalAuthenticationProvider.authenticate(authentication);
+ }
+
+ protected Injector getInjector() {
+ return Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ Configuration configuration = createNiceMock(Configuration.class);
+ expect(configuration.getMaxAuthenticationFailures()).andReturn(10).anyTimes();
+ expect(configuration.showLockedOutUserMessage()).andReturn(true).anyTimes();
+
+ bind(EntityManager.class).toInstance(createMock(EntityManager.class));
+ bind(DBAccessor.class).toInstance(createMock(DBAccessor.class));
+ bind(OsFamily.class).toInstance(createNiceMock(OsFamily.class));
+ bind(HookService.class).toInstance(createMock(HookService.class));
+ bind(HookContextFactory.class).toInstance(createMock(HookContextFactory.class));
+
+ bind(Users.class).toInstance(createMock(Users.class));
+ bind(Configuration.class).toInstance(configuration);
+ }
+ }, getAdditionalModule());
+
+ }
+
+ protected abstract AuthenticationProvider getAuthenticationProvider(Injector injector);
+
+ protected abstract Authentication getAuthentication(boolean correctUsername, boolean correctCredential);
+
+ protected abstract UserEntity getUserEntity(Injector injector, String username, int consecutiveFailures, boolean active);
+
+ protected abstract Module getAdditionalModule();
+
+ protected abstract void validateAuthenticationResult(AmbariUserAuthentication result);
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/e1699b09/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProviderTest.java
new file mode 100644
index 0000000..d445c07
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authentication/AmbariLocalAuthenticationProviderTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Collections;
+
+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.authorization.AmbariUserAuthentication;
+import org.apache.ambari.server.security.authorization.UserAuthenticationType;
+import org.apache.ambari.server.security.authorization.UserName;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.crypto.password.StandardPasswordEncoder;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+public class AmbariLocalAuthenticationProviderTest extends AbstractAuthenticationProviderTest {
+
+ private static final String TEST_USER_PASS = "userPass";
+ private static final String TEST_USER_INCORRECT_PASS = "userIncorrectPass";
+
+ @Override
+ protected AuthenticationProvider getAuthenticationProvider(Injector injector) {
+ return injector.getInstance(AmbariLocalAuthenticationProvider.class);
+ }
+
+ @Override
+ protected Authentication getAuthentication(boolean correctUsername, boolean correctCredential) {
+ return new UsernamePasswordAuthenticationToken(
+ correctUsername ? TEST_USER_NAME : "incorrect_username",
+ correctCredential ? TEST_USER_PASS : TEST_USER_INCORRECT_PASS
+ );
+ }
+
+ @Override
+ protected UserEntity getUserEntity(Injector injector, String username, int consecutiveFailures, boolean active) {
+ PrincipalEntity principalEntity = new PrincipalEntity();
+
+ UserAuthenticationEntity userAuthenticationEntity = new UserAuthenticationEntity();
+ userAuthenticationEntity.setAuthenticationType(UserAuthenticationType.LOCAL);
+ userAuthenticationEntity.setAuthenticationKey(injector.getInstance(PasswordEncoder.class).encode(TEST_USER_PASS));
+
+ UserEntity userEntity = new UserEntity();
+ userEntity.setUserId(1);
+ userEntity.setUserName(UserName.fromString(username).toString());
+ userEntity.setPrincipal(principalEntity);
+ userEntity.setAuthenticationEntities(Collections.singletonList(userAuthenticationEntity));
+ userEntity.setConsecutiveFailures(consecutiveFailures);
+ userEntity.setActive(active);
+ return userEntity;
+ }
+
+ @Override
+ protected Module getAdditionalModule() {
+ return new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(PasswordEncoder.class).toInstance(new StandardPasswordEncoder());
+ }
+ };
+ }
+
+ @Override
+ protected void validateAuthenticationResult(AmbariUserAuthentication result) {
+ assertEquals(1, (result.getPrincipal()).getUserId());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/e1699b09/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationProviderDisableUserTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationProviderDisableUserTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationProviderDisableUserTest.java
deleted file mode 100644
index fea7fb9..0000000
--- a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationProviderDisableUserTest.java
+++ /dev/null
@@ -1,108 +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 org.apache.ambari.server.security.authorization;
-
-import java.util.Collections;
-
-import org.apache.ambari.server.orm.dao.MemberDAO;
-import org.apache.ambari.server.orm.dao.PrivilegeDAO;
-import org.apache.ambari.server.orm.dao.UserDAO;
-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.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.crypto.password.PasswordEncoder;
-import org.springframework.security.crypto.password.StandardPasswordEncoder;
-
-public class AmbariAuthorizationProviderDisableUserTest {
-
- private Users users;
-
- private UserDAO userDAO;
-
- private PasswordEncoder encoder = new StandardPasswordEncoder();
-
- private AmbariLocalUserProvider alup;
-
- private AmbariLdapAuthoritiesPopulator ldapPopulator;
-
- @Before
- public void setUp() {
- userDAO = Mockito.mock(UserDAO.class);
- users = Mockito.mock(Users.class);
-
- createUser("activeUser", true);
- createUser("disabledUser", false);
-
- MemberDAO memberDao = Mockito.mock(MemberDAO.class);
- PrivilegeDAO privilegeDao = Mockito.mock(PrivilegeDAO.class);
- AuthorizationHelper authorizationHelper = new AuthorizationHelper();
-
- alup = new AmbariLocalUserProvider(userDAO, users, encoder);
-
- ldapPopulator = new AmbariLdapAuthoritiesPopulator(authorizationHelper, userDAO, memberDao, privilegeDao, users);
-
- }
-
- @Test public void testDisabledUserViaDaoProvider(){
- try {
- alup.authenticate(new UsernamePasswordAuthenticationToken("disabledUser","pwd"));
- Assert.fail("Disabled user passes authentication");
- } catch (InvalidUsernamePasswordCombinationException e){
- //expected
- Assert.assertEquals(InvalidUsernamePasswordCombinationException.MESSAGE, e.getMessage());//UI depends on this
- }
- Authentication auth = alup.authenticate(new UsernamePasswordAuthenticationToken("activeUser","pwd"));
- Assert.assertNotNull(auth);
- Assert.assertTrue(auth.isAuthenticated());
- }
-
- @Test public void testDisabledUserViaLdapProvider(){
- try {
- ldapPopulator.getGrantedAuthorities(null, "disabledUser");
- Assert.fail("Disabled user passes authentication");
- } catch (InvalidUsernamePasswordCombinationException e) {
- //expected
- Assert.assertEquals(InvalidUsernamePasswordCombinationException.MESSAGE, e.getMessage());//UI depends on this
- }
- }
-
- private void createUser(String login, boolean isActive) {
- PrincipalEntity principalEntity = new PrincipalEntity();
-
- UserAuthenticationEntity userAuthenticationEntity = new UserAuthenticationEntity();
- userAuthenticationEntity.setAuthenticationType(UserAuthenticationType.LOCAL);
- userAuthenticationEntity.setAuthenticationKey(encoder.encode("pwd"));
-
- UserEntity activeUser = new UserEntity();
- activeUser.setUserId(1);
- activeUser.setActive(isActive);
- activeUser.setUserName(UserName.fromString(login).toString());
- activeUser.setAuthenticationEntities(Collections.singletonList(userAuthenticationEntity));
- activeUser.setPrincipal(principalEntity);
- Mockito.when(userDAO.findUserByName(login)).thenReturn(activeUser);
- Mockito.when(userDAO.findUserByName(login)).thenReturn(activeUser);
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/e1699b09/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
deleted file mode 100644
index fb4ebf9..0000000
--- a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLocalUserProviderTest.java
+++ /dev/null
@@ -1,190 +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 org.apache.ambari.server.security.authorization;
-
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import java.util.Collections;
-
-import org.apache.ambari.server.H2DatabaseCleaner;
-import org.apache.ambari.server.audit.AuditLoggerModule;
-import org.apache.ambari.server.orm.GuiceJpaInitializer;
-import org.apache.ambari.server.orm.OrmTestHelper;
-import org.apache.ambari.server.orm.dao.UserDAO;
-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;
-import org.junit.Test;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.crypto.password.PasswordEncoder;
-
-import com.google.inject.Guice;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-
-public class AmbariLocalUserProviderTest {
- private static Injector injector;
-
- @Inject
- PasswordEncoder passwordEncoder;
-
- private static final String TEST_USER_NAME = "userName";
- private static final String TEST_USER_PASS = "userPass";
- private static final String TEST_USER_INCORRECT_PASS = "userIncorrectPass";
-
- @BeforeClass
- public static void prepareData() {
- injector = Guice.createInjector(new AuditLoggerModule(), new AuthorizationTestModule());
- injector.getInstance(GuiceJpaInitializer.class);
- injector.getInstance(OrmTestHelper.class).createTestUsers();
- }
-
- @Before
- public void setUp() throws Exception {
- injector.injectMembers(this);
- SecurityContextHolder.getContext().setAuthentication(null);
- }
-
- @AfterClass
- public static void tearDown() throws Exception {
- H2DatabaseCleaner.clearDatabaseAndStopPersistenceService(injector);
- }
-
- @Test
- public void testSuccessfulAuth() {
- Users users = createMock(Users.class);
- UserDAO userDAO = createMock(UserDAO.class);
- Authentication authentication = createMock(Authentication.class);
-
- UserEntity userEntity = combineUserEntity();
-
- expect(authentication.getName()).andReturn(TEST_USER_NAME);
- expect(userDAO.findUserByName(TEST_USER_NAME)).andReturn(userEntity);
- expect(authentication.getCredentials()).andReturn(TEST_USER_PASS).anyTimes();
- expect(users.getUserAuthorities(userEntity)).andReturn(null);
-
- replay(users, userDAO, authentication);
-
- AmbariLocalUserProvider ambariLocalUserProvider = new AmbariLocalUserProvider(userDAO, users, passwordEncoder);
- Authentication resultedAuth = ambariLocalUserProvider.authenticate(authentication);
-
- verify(users, userDAO, authentication);
-
- assertNotNull(resultedAuth);
- assertEquals(true, resultedAuth.isAuthenticated());
- assertTrue(resultedAuth instanceof AmbariUserAuthentication);
- assertEquals(1, ((User) resultedAuth.getPrincipal()).getUserId());
- }
-
- @Test(expected = InvalidUsernamePasswordCombinationException.class)
- public void testAuthWithIncorrectName() {
- Users users = createMock(Users.class);
- UserDAO userDAO = createMock(UserDAO.class);
- Authentication authentication = createMock(Authentication.class);
-
- expect(authentication.getName()).andReturn(TEST_USER_NAME);
- expect(userDAO.findUserByName(TEST_USER_NAME)).andReturn(null);
-
- replay(users, userDAO, authentication);
-
- AmbariLocalUserProvider ambariLocalUserProvider = new AmbariLocalUserProvider(userDAO, users, passwordEncoder);
- ambariLocalUserProvider.authenticate(authentication);
- }
-
- @Test(expected = InvalidUsernamePasswordCombinationException.class)
- public void testAuthWithoutPass() {
- Users users = createMock(Users.class);
- UserDAO userDAO = createMock(UserDAO.class);
- Authentication authentication = createMock(Authentication.class);
-
- UserEntity userEntity = combineUserEntity();
-
- expect(authentication.getName()).andReturn(TEST_USER_NAME);
- expect(userDAO.findUserByName(TEST_USER_NAME)).andReturn(userEntity);
- expect(authentication.getCredentials()).andReturn(null);
-
- replay(users, userDAO, authentication);
-
- AmbariLocalUserProvider ambariLocalUserProvider = new AmbariLocalUserProvider(userDAO, users, passwordEncoder);
- ambariLocalUserProvider.authenticate(authentication);
- }
-
- @Test(expected = InvalidUsernamePasswordCombinationException.class)
- public void testAuthWithIncorrectPass() {
- Users users = createMock(Users.class);
- UserDAO userDAO = createMock(UserDAO.class);
- Authentication authentication = createMock(Authentication.class);
-
- UserEntity userEntity = combineUserEntity();
-
- expect(authentication.getName()).andReturn(TEST_USER_NAME);
- expect(userDAO.findUserByName(TEST_USER_NAME)).andReturn(userEntity);
- expect(authentication.getCredentials()).andReturn(TEST_USER_INCORRECT_PASS).anyTimes();
-
- replay(users, userDAO, authentication);
-
- AmbariLocalUserProvider ambariLocalUserProvider = new AmbariLocalUserProvider(userDAO, users, passwordEncoder);
- 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();
-
- UserAuthenticationEntity userAuthenticationEntity = new UserAuthenticationEntity();
- userAuthenticationEntity.setAuthenticationType(UserAuthenticationType.LOCAL);
- userAuthenticationEntity.setAuthenticationKey(passwordEncoder.encode(TEST_USER_PASS));
-
- UserEntity userEntity = new UserEntity();
- userEntity.setUserId(1);
- userEntity.setUserName(UserName.fromString(TEST_USER_NAME).toString());
- userEntity.setPrincipal(principalEntity);
- userEntity.setAuthenticationEntities(Collections.singletonList(userAuthenticationEntity));
- return userEntity;
- }
-}