Repository: ambari Updated Branches: refs/heads/branch-feature-AMBARI-20859 de4deaf0b -> 637c52197
AMBARI-21219. Update LDAP 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/637c5219 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/637c5219 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/637c5219 Branch: refs/heads/branch-feature-AMBARI-20859 Commit: 637c521972cb3a064bd108e97a1007be10d7ef09 Parents: de4deaf Author: Robert Levas <[email protected]> Authored: Wed Dec 6 10:00:48 2017 -0500 Committer: Robert Levas <[email protected]> Committed: Wed Dec 6 10:00:48 2017 -0500 ---------------------------------------------------------------------- ambari-server/docs/configuration/index.md | 6 +- .../server/configuration/Configuration.java | 45 ++- .../AmbariLdapConfigurationProvider.java | 3 +- .../apache/ambari/server/orm/dao/UserDAO.java | 2 +- .../AmbariAuthenticationProvider.java | 37 ++ .../authorization/AmbariAuthentication.java | 229 ------------- .../AmbariLdapAuthenticationProvider.java | 186 ++++++++--- .../authorization/LdapServerProperties.java | 11 + .../server/security/authorization/Users.java | 86 +++-- .../server/configuration/ConfigurationTest.java | 6 + .../authorization/AmbariAuthenticationTest.java | 334 ------------------- ...henticationProviderForDuplicateUserTest.java | 13 +- .../AmbariLdapAuthenticationProviderTest.java | 11 +- .../authorization/AuthorizationHelperTest.java | 23 -- .../security/authorization/TestUsers.java | 3 - 15 files changed, 305 insertions(+), 690 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/ambari-server/docs/configuration/index.md ---------------------------------------------------------------------- diff --git a/ambari-server/docs/configuration/index.md b/ambari-server/docs/configuration/index.md index 514e9ed..26a2240 100644 --- a/ambari-server/docs/configuration/index.md +++ b/ambari-server/docs/configuration/index.md @@ -83,7 +83,6 @@ The following are the properties which can be used to configure Ambari. | authentication.kerberos.enabled | Determines whether to use Kerberos (SPNEGO) authentication when connecting Ambari. |`false` | | authentication.kerberos.spnego.keytab.file | The Kerberos keytab file to use when verifying user-supplied Kerberos tokens for authentication via SPNEGO |`/etc/security/keytabs/spnego.service.keytab` | | authentication.kerberos.spnego.principal | The Kerberos principal name to use when verifying user-supplied Kerberos tokens for authentication via SPNEGO |`HTTP/_HOST` | -| authentication.kerberos.user.types | A comma-delimited (ordered) list of preferred user types to use when finding the Ambari user account for the user-supplied Kerberos identity during authentication via SPNEGO |`LDAP` | | authentication.ldap.alternateUserSearchEnabled | Determines whether a secondary (alternate) LDAP user search filer is used if the primary filter fails to find a user. |`false` | | authentication.ldap.alternateUserSearchFilter | An alternate LDAP user search filter which can be used if `authentication.ldap.alternateUserSearchEnabled` is enabled and the primary filter fails to find a user. |`(&(userPrincipalName={0})(objectClass={userObjectClass}))` | | authentication.ldap.baseDn | The base DN to use when filtering LDAP users and groups. This is only used when LDAP authentication is enabled. |`dc=ambari,dc=apache,dc=org` | @@ -137,6 +136,7 @@ The following are the properties which can be used to configure Ambari. | db.oracle.jdbc.name | The name of the Oracle JDBC JAR connector. |`ojdbc6.jar` | | default.kdcserver.port | The port used to communicate with the Kerberos Key Distribution Center. |`88` | | extensions.path | The location on the Ambari Server where stack extensions exist.<br/><br/>The following are examples of valid values:<ul><li>`/var/lib/ambari-server/resources/extensions`</ul> | | +| gpl.license.accepted | Whether user accepted GPL license. |`false` | | http.cache-control | The value that will be used to set the `Cache-Control` HTTP response header. |`no-store` | | http.charset | The value that will be used to set the Character encoding to HTTP response header. |`utf-8` | | http.pragma | The value that will be used to set the `PRAGMA` HTTP response header. |`no-cache` | @@ -153,7 +153,7 @@ The following are the properties which can be used to configure Ambari. | kerberos.operation.retries | The number of times failed Kerberos operations should be retried to execute. |`3` | | kerberos.operation.retry.timeout | The time to wait (in seconds) between failed Kerberos operations retries. |`10` | | kerberos.operation.verify.kdc.trust | Validate the trust of the SSL certificate provided by the KDC when performing Kerberos operations over SSL. |`true` | -| ldap.sync.username.collision.behavior | Determines how to handle username collision while updating from LDAP.<br/><br/>The following are examples of valid values:<ul><li>`skip`<li>`convert`</ul> |`convert` | +| ldap.sync.username.collision.behavior | Determines how to handle username collision while updating from LDAP.<br/><br/>The following are examples of valid values:<ul><li>`skip`<li>`convert`<li>`add`</ul> |`add` | | log4j.monitor.delay | Indicates the delay, in milliseconds, for the log4j monitor to check for changes |`300000` | | logsearch.metadata.cache.expire.timeout | The time, in hours, that the Ambari Server will hold Log File metadata in its internal cache before making a request to the LogSearch Portal to get the latest metadata. |`24` | | logsearch.portal.connect.timeout | The time, in milliseconds, that the Ambari Server will wait while attempting to connect to the LogSearch Portal service. |`5000` | @@ -180,6 +180,7 @@ The following are the properties which can be used to configure Ambari. | recovery.window_in_minutes | The length of a recovery window, in minutes, in which recovery attempts can be retried.<br/><br/> This property is related to `recovery.max_count`. | | | repo.validation.suffixes.default | The suffixes to use when validating most types of repositories. |`/repodata/repomd.xml` | | repo.validation.suffixes.ubuntu | The suffixes to use when validating Ubuntu repositories. |`/dists/%s/Release` | +| repositories.legacy-override.enabled | This property is used in specific testing circumstances only. Its use otherwise will lead to very unpredictable results with repository management and package installation |`false` | | resources.dir | The location on the Ambari Server where all resources exist, including common services, stacks, and scripts. |`/var/lib/ambari-server/resources/` | | rolling.upgrade.skip.packages.prefixes | A comma-separated list of packages which will be skipped during a stack upgrade. | | | security.agent.hostname.validate | Determines whether the Ambari Agent host names should be validated against a regular expression to ensure that they are well-formed.<br><br>WARNING: By setting this value to false, host names will not be validated, allowing a possible security vulnerability as described in CVE-2014-3582. See https://cwiki.apache.org/confluence/display/AMBARI/Ambari+Vulnerabilities for more information. |`true` | @@ -284,6 +285,7 @@ The following are the properties which can be used to configure Ambari. | ssl.trustStore.password | The password to use when setting the `javax.net.ssl.trustStorePassword` property | | | ssl.trustStore.path | The location of the truststore to use when setting the `javax.net.ssl.trustStore` property. | | | ssl.trustStore.type | The type of truststore used by the `javax.net.ssl.trustStoreType` property. | | +| stack.hooks.folder | A location of hooks folder relative to resources folder. |`stack-hooks` | | stack.java.home | The location of the JDK on the Ambari Agent hosts for stack services.<br/><br/>The following are examples of valid values:<ul><li>`/usr/jdk64/jdk1.7.0_45`</ul> | | | stack.java.version | JDK version of the stack, use in case of it differs from Ambari JDK version.<br/><br/>The following are examples of valid values:<ul><li>`1.7`</ul> | | | stack.jce.name | The name of the JCE policy ZIP file for stack services.<br/><br/>The following are examples of valid values:<ul><li>`UnlimitedJCEPolicyJDK7.zip`</ul> | | http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/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 8904199..2c203c9 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 @@ -696,10 +696,10 @@ public class Configuration { */ @Markdown( description = "Determines how to handle username collision while updating from LDAP.", - examples = { "skip", "convert" } + examples = {"skip", "convert", "add"} ) public static final ConfigurationProperty<String> LDAP_SYNC_USERNAME_COLLISIONS_BEHAVIOR = new ConfigurationProperty<>( - "ldap.sync.username.collision.behavior", "convert"); + "ldap.sync.username.collision.behavior", "add"); /** * The location on the Ambari Server where stack extensions exist. @@ -2826,12 +2826,38 @@ public class Configuration { /** * Ldap username collision handling behavior. - * CONVERT - convert existing local users to LDAP users. + * ADD - append the new LDAP entry to the set of existing authentication methods. + * CONVERT - remove all authentication methods except for the new LDAP entry. * SKIP - skip existing local users. */ public enum LdapUsernameCollisionHandlingBehavior { + ADD, CONVERT, - SKIP + SKIP; + + /** + * Safely translates a user-supplied behavior name to a {@link LdapUsernameCollisionHandlingBehavior}. + * <p> + * If the user-supplied value is empty or invalid, the default value is returned. + * + * @param value a user-supplied behavior name value + * @param defaultValue the default value + * @return a {@link LdapUsernameCollisionHandlingBehavior} + */ + public static LdapUsernameCollisionHandlingBehavior translate(String value, LdapUsernameCollisionHandlingBehavior defaultValue) { + String processedValue = StringUtils.upperCase(StringUtils.trim(value)); + + if (StringUtils.isEmpty(processedValue)) { + return defaultValue; + } else { + try { + return valueOf(processedValue); + } catch (IllegalArgumentException e) { + LOG.warn("Invalid LDAP username collision value ({}), using the default value ({})", value, defaultValue.name().toLowerCase()); + return defaultValue; + } + } + } } /** @@ -4109,6 +4135,7 @@ public class Configuration { /** * Gets parameters of LDAP server to connect to + * * @return LdapServerProperties object representing connection parameters */ public LdapServerProperties getLdapServerProperties() { @@ -4145,6 +4172,7 @@ public class Configuration { ldapServerProperties.setAdminGroupMappingRules(getProperty(LDAP_ADMIN_GROUP_MAPPING_RULES)); ldapServerProperties.setAdminGroupMappingMemberAttr(getProperty(LDAP_ADMIN_GROUP_MAPPING_MEMBER_ATTR_DEFAULT)); ldapServerProperties.setUserSearchFilter(getProperty(LDAP_USER_SEARCH_FILTER)); + ldapServerProperties.setAlternateUserSearchFilterEnabled(Boolean.parseBoolean(getProperty(LDAP_ALT_USER_SEARCH_ENABLED))); ldapServerProperties.setAlternateUserSearchFilter(getProperty(LDAP_ALT_USER_SEARCH_FILTER)); ldapServerProperties.setGroupSearchFilter(getProperty(LDAP_GROUP_SEARCH_FILTER)); ldapServerProperties.setReferralMethod(getProperty(LDAP_REFERRAL)); @@ -5012,8 +5040,6 @@ public class Configuration { } /** - - /** * Gets the default KDC port to use when no port is specified in KDC hostname * * @return the default KDC port to use. @@ -5057,10 +5083,9 @@ public class Configuration { * @return true if ambari need to skip existing user during LDAP sync. */ public LdapUsernameCollisionHandlingBehavior getLdapSyncCollisionHandlingBehavior() { - if (getProperty(LDAP_SYNC_USERNAME_COLLISIONS_BEHAVIOR).toLowerCase().equals("skip")) { - return LdapUsernameCollisionHandlingBehavior.SKIP; - } - return LdapUsernameCollisionHandlingBehavior.CONVERT; + return LdapUsernameCollisionHandlingBehavior.translate( + getProperty(LDAP_SYNC_USERNAME_COLLISIONS_BEHAVIOR), + LdapUsernameCollisionHandlingBehavior.ADD); } /** http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapConfigurationProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapConfigurationProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapConfigurationProvider.java index f1e1881..fa2a927 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapConfigurationProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/ldap/service/AmbariLdapConfigurationProvider.java @@ -28,7 +28,6 @@ import org.apache.ambari.server.events.publishers.AmbariEventPublisher; import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration; import org.apache.ambari.server.orm.dao.AmbariConfigurationDAO; import org.apache.ambari.server.orm.entities.AmbariConfigurationEntity; -import org.apache.ambari.server.security.authorization.AmbariLdapAuthenticationProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +49,7 @@ import com.google.inject.Singleton; @Singleton public class AmbariLdapConfigurationProvider implements Provider<AmbariLdapConfiguration> { - private static final Logger LOGGER = LoggerFactory.getLogger(AmbariLdapAuthenticationProvider.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AmbariLdapConfigurationProvider.class); private AmbariLdapConfiguration instance; @Inject http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UserDAO.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UserDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UserDAO.java index 0e28e50..bbddb90 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UserDAO.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UserDAO.java @@ -105,7 +105,7 @@ public class UserDAO { @Transactional public void create(Set<UserEntity> users) { - for (UserEntity user: users) { + for (UserEntity user : users) { entityManagerProvider.get().persist(user); } } http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/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 index 0e5c913..0a64332 100644 --- 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 @@ -18,6 +18,7 @@ package org.apache.ambari.server.security.authentication; +import java.util.ArrayList; import java.util.Collection; import org.apache.ambari.server.configuration.Configuration; @@ -70,6 +71,42 @@ public abstract class AmbariAuthenticationProvider implements AuthenticationProv return null; } + /** + * Finds the specific set of {@link UserAuthenticationEntity} instances 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 collection {@link UserAuthenticationEntity} if found; otherwise null + */ + protected Collection<UserAuthenticationEntity> getAuthenticationEntities(UserEntity userEntity, UserAuthenticationType type) { + Collection<UserAuthenticationEntity> foundAuthenticationEntities = null; + + Collection<UserAuthenticationEntity> authenticationEntities = (userEntity == null) ? null : userEntity.getAuthenticationEntities(); + if (authenticationEntities != null) { + foundAuthenticationEntities = new ArrayList<>(); + for (UserAuthenticationEntity authenticationEntity : authenticationEntities) { + if (authenticationEntity.getAuthenticationType() == type) { + foundAuthenticationEntities.add(authenticationEntity); + } + } + } + + return foundAuthenticationEntities; + } + + /** + * Finds the specific set of {@link UserAuthenticationEntity} instances from the collection of + * authentication methods available to the specified {@link UserEntity}. + * + * @param type the {@link UserAuthenticationType} to retrieve + * @param key the key to match on + * @return a collection {@link UserAuthenticationEntity} if found; otherwise null + */ + protected Collection<UserAuthenticationEntity> getAuthenticationEntities(UserAuthenticationType type, String key) { + return users.getUserAuthenticationEntities(type, key); + } + protected Users getUsers() { return users; } http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthentication.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthentication.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthentication.java deleted file mode 100644 index bf30b83..0000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthentication.java +++ /dev/null @@ -1,229 +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.security.Principal; -import java.util.Collection; -import java.util.Objects; - -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; - -/** - * This class is a wrapper for authentication objects to - * provide functionality for resolving login aliases to - * ambari user names. - */ -public final class AmbariAuthentication implements Authentication, UserIdAuthentication { - private final Authentication authentication; - private final Object principalOverride; - private final Integer userId; - - public AmbariAuthentication(Authentication authentication, Integer userId) { - this.authentication = authentication; - this.principalOverride = getPrincipalOverride(); - this.userId = userId; - } - - - - /** - * Set by an <code>AuthenticationManager</code> to indicate the authorities that the principal has been - * granted. Note that classes should not rely on this value as being valid unless it has been set by a trusted - * <code>AuthenticationManager</code>. - * <p> - * Implementations should ensure that modifications to the returned collection - * array do not affect the state of the Authentication object, or use an unmodifiable instance. - * </p> - * - * @return the authorities granted to the principal, or an empty collection if the token has not been authenticated. - * Never null. - */ - @Override - public Collection<? extends GrantedAuthority> getAuthorities() { - return authentication.getAuthorities(); - } - - /** - * The credentials that prove the principal is correct. This is usually a password, but could be anything - * relevant to the <code>AuthenticationManager</code>. Callers are expected to populate the credentials. - * - * @return the credentials that prove the identity of the <code>Principal</code> - */ - @Override - public Object getCredentials() { - return authentication.getCredentials(); - } - - /** - * Stores additional details about the authentication request. These might be an IP address, certificate - * serial number etc. - * - * @return additional details about the authentication request, or <code>null</code> if not used - */ - @Override - public Object getDetails() { - return authentication.getDetails(); - } - - /** - * The identity of the principal being authenticated. In the case of an authentication request with username and - * password, this would be the username. Callers are expected to populate the principal for an authentication - * request. - * <p> - * The <tt>AuthenticationManager</tt> implementation will often return an <tt>Authentication</tt> containing - * richer information as the principal for use by the application. Many of the authentication providers will - * create a {@code UserDetails} object as the principal. - * - * @return the <code>Principal</code> being authenticated or the authenticated principal after authentication. - */ - @Override - public Object getPrincipal() { - if (principalOverride != null) { - return principalOverride; - } - - return authentication.getPrincipal(); - } - - /** - * Used to indicate to {@code AbstractSecurityInterceptor} whether it should present the - * authentication token to the <code>AuthenticationManager</code>. Typically an <code>AuthenticationManager</code> - * (or, more often, one of its <code>AuthenticationProvider</code>s) will return an immutable authentication token - * after successful authentication, in which case that token can safely return <code>true</code> to this method. - * Returning <code>true</code> will improve performance, as calling the <code>AuthenticationManager</code> for - * every request will no longer be necessary. - * <p> - * For security reasons, implementations of this interface should be very careful about returning - * <code>true</code> from this method unless they are either immutable, or have some way of ensuring the properties - * have not been changed since original creation. - * - * @return true if the token has been authenticated and the <code>AbstractSecurityInterceptor</code> does not need - * to present the token to the <code>AuthenticationManager</code> again for re-authentication. - */ - @Override - public boolean isAuthenticated() { - return authentication.isAuthenticated(); - } - - /** - * See {@link #isAuthenticated()} for a full description. - * <p> - * Implementations should <b>always</b> allow this method to be called with a <code>false</code> parameter, - * as this is used by various classes to specify the authentication token should not be trusted. - * If an implementation wishes to reject an invocation with a <code>true</code> parameter (which would indicate - * the authentication token is trusted - a potential security risk) the implementation should throw an - * {@link IllegalArgumentException}. - * - * @param isAuthenticated <code>true</code> if the token should be trusted (which may result in an exception) or - * <code>false</code> if the token should not be trusted - * @throws IllegalArgumentException if an attempt to make the authentication token trusted (by passing - * <code>true</code> as the argument) is rejected due to the implementation being immutable or - * implementing its own alternative approach to {@link #isAuthenticated()} - */ - @Override - public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { - authentication.setAuthenticated(isAuthenticated); - } - - /** - * Returns the name of this principal. - * - * @return the name of this principal. - */ - @Override - public String getName() { - if (principalOverride != null) - { - if (principalOverride instanceof UserDetails) { - return ((UserDetails) principalOverride).getUsername(); - } - - return principalOverride.toString(); - } - - return authentication.getName(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AmbariAuthentication that = (AmbariAuthentication) o; - return Objects.equals(authentication, that.authentication) && - Objects.equals(principalOverride, that.principalOverride); - } - - @Override - public int hashCode() { - return Objects.hash(authentication, principalOverride); - } - - /** - * Returns a principal object that is to be used - * to override the original principal object - * returned by the inner {@link #authentication} object. - * - * <p>The purpose of overriding the origin principal is to provide - * and object that resolves the contained user name to ambari user name in case - * the original user name is a login alias.</p> - * - * @return principal override of the original one is of type {@link UserDetails}, - * if the original one is a login alias name than the user name the login alias resolves to - * otherwise <code>null</code> - */ - private Object getPrincipalOverride() { - Object principal = authentication.getPrincipal(); - - if (principal instanceof UserDetails) { - UserDetails user = (UserDetails)principal; - String usernameOrig = user.getUsername(); - String username = AuthorizationHelper.resolveLoginAliasToUserName(usernameOrig); - - if (username.equals(usernameOrig)) - return null; // create override only original username is a login alias - - - String userPassword = user.getPassword() != null ? user.getPassword() : ""; - - principal = - new User( - username, - userPassword, - user.isEnabled(), - user.isAccountNonExpired(), - user.isCredentialsNonExpired(), - user.isAccountNonLocked(), - user.getAuthorities()); - } else if ( !(principal instanceof Principal) && principal != null ){ - String username = principal.toString(); - principal = AuthorizationHelper.resolveLoginAliasToUserName(username); - } else { - principal = null; - } - - return principal; - } - - @Override - public Integer getUserId() { - return userId; - } -} http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProvider.java index a6f4387..20a06cc 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProvider.java @@ -17,25 +17,31 @@ */ package org.apache.ambari.server.security.authorization; +import java.util.Collection; import java.util.List; import org.apache.ambari.server.configuration.Configuration; -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.ClientSecurityType; +import org.apache.ambari.server.security.authentication.AccountDisabledException; +import org.apache.ambari.server.security.authentication.AmbariAuthenticationProvider; +import org.apache.ambari.server.security.authentication.AmbariUserAuthentication; import org.apache.ambari.server.security.authentication.InvalidUsernamePasswordCombinationException; +import org.apache.ambari.server.security.authentication.TooManyLoginFailuresException; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.ldap.core.support.LdapContextSource; -import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; +import org.springframework.security.ldap.userdetails.LdapUserDetails; import com.google.inject.Inject; @@ -43,39 +49,64 @@ import com.google.inject.Inject; /** * Provides LDAP user authorization logic for Ambari Server */ -public class AmbariLdapAuthenticationProvider implements AuthenticationProvider { +public class AmbariLdapAuthenticationProvider extends AmbariAuthenticationProvider { static Logger LOG = LoggerFactory.getLogger(AmbariLdapAuthenticationProvider.class); // exposed and mutable for "test" - Configuration configuration; - private AmbariLdapAuthoritiesPopulator authoritiesPopulator; - private UserDAO userDAO; private ThreadLocal<LdapServerProperties> ldapServerProperties = new ThreadLocal<>(); private ThreadLocal<LdapAuthenticationProvider> providerThreadLocal = new ThreadLocal<>(); private ThreadLocal<String> ldapUserSearchFilterThreadLocal = new ThreadLocal<>(); @Inject - public AmbariLdapAuthenticationProvider(Configuration configuration, - AmbariLdapAuthoritiesPopulator authoritiesPopulator, UserDAO userDAO) { - this.configuration = configuration; + public AmbariLdapAuthenticationProvider(Users users, AmbariLdapAuthoritiesPopulator authoritiesPopulator, Configuration configuration) { + super(users, configuration); this.authoritiesPopulator = authoritiesPopulator; - this.userDAO = userDAO; } - // TODO: ************ - // TODO: This is to be revisited for AMBARI-21219 (Update LDAP Authentication process to work with improved user management facility) - // TODO: ************ @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { if (isLdapEnabled()) { - String username = getUserName(authentication); + if (authentication.getName() == null) { + LOG.info("Authentication failed: no username provided"); + throw new InvalidUsernamePasswordCombinationException(""); + } + + String username = authentication.getName().trim(); + + if (authentication.getCredentials() == null) { + LOG.info("Authentication failed: no credentials provided: {}", username); + throw new InvalidUsernamePasswordCombinationException(username); + } try { Authentication auth = loadLdapAuthenticationProvider(username).authenticate(authentication); - Integer userId = getUserId(auth); + UserEntity userEntity = getUserEntity(auth); + + if (userEntity == null) { + // TODO: If we were automatically importing accounts from the LDAP server, we should + // TODO: probably do it here. + LOG.debug("user not found ('{}')", username); + throw new InvalidUsernamePasswordCombinationException(username); + } else { + Users users = getUsers(); + + // Ensure the user is allowed to login.... + try { + users.validateLogin(userEntity, username); + } catch (AccountDisabledException | TooManyLoginFailuresException e) { + if (getConfiguration().showLockedOutUserMessage()) { + throw e; + } else { + // Do not give away information about the existence or status of a user + throw new InvalidUsernamePasswordCombinationException(username, false, e); + } + } - return new AmbariAuthentication(auth, userId); + Authentication authToken = new AmbariUserAuthentication(null, users.getUser(userEntity), users.getUserAuthorities(userEntity)); + authToken.setAuthenticated(true); + return authToken; + } } catch (AuthenticationException e) { LOG.debug("Got exception during LDAP authentication attempt", e); // Try to help in troubleshooting @@ -96,9 +127,9 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider } throw new InvalidUsernamePasswordCombinationException(username, e); } catch (IncorrectResultSizeDataAccessException multipleUsersFound) { - String message = configuration.isLdapAlternateUserSearchEnabled() ? - String.format("Login Failed: Please append your domain to your username and try again. Example: %s@domain", username) : - "Login Failed: More than one user with that username found, please work with your Ambari Administrator to adjust your LDAP configuration"; + String message = getConfiguration().isLdapAlternateUserSearchEnabled() ? + String.format("Login Failed: Please append your domain to your username and try again. Example: %s@domain", username) : + "Login Failed: More than one user with that username found, please work with your Ambari Administrator to adjust your LDAP configuration"; throw new DuplicateLdapUserFoundAuthenticationException(message); } @@ -114,6 +145,7 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider /** * Reloads LDAP Context Source and depending objects if properties were changed + * * @return corresponding LDAP authentication provider */ LdapAuthenticationProvider loadLdapAuthenticationProvider(String userName) { @@ -121,7 +153,7 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider String ldapUserSearchFilter = getLdapUserSearchFilter(userName); - if (ldapConfigPropertiesChanged|| !ldapUserSearchFilter.equals(ldapUserSearchFilterThreadLocal.get())) { + if (ldapConfigPropertiesChanged || !ldapUserSearchFilter.equals(ldapUserSearchFilterThreadLocal.get())) { LOG.info("Either LDAP Properties or user search filter changed - rebuilding Context"); LdapContextSource springSecurityContextSource = new LdapContextSource(); @@ -145,7 +177,7 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider String userSearchBase = ldapServerProperties.get().getUserSearchBase(); FilterBasedLdapUserSearch userSearch = new FilterBasedLdapUserSearch(userSearchBase, ldapUserSearchFilter, springSecurityContextSource); - AmbariLdapBindAuthenticator bindAuthenticator = new AmbariLdapBindAuthenticator(springSecurityContextSource, configuration); + AmbariLdapBindAuthenticator bindAuthenticator = new AmbariLdapBindAuthenticator(springSecurityContextSource, getConfiguration()); bindAuthenticator.setUserSearch(userSearch); LdapAuthenticationProvider authenticationProvider = new LdapAuthenticationProvider(bindAuthenticator, authoritiesPopulator); @@ -160,20 +192,11 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider /** * Check if LDAP authentication is enabled in server properties + * * @return true if enabled */ boolean isLdapEnabled() { - return configuration.getClientSecurityType() == ClientSecurityType.LDAP; - } - - /** - * Extracts the user name from the passed authentication object. - * @param authentication - * @return - */ - protected String getUserName(Authentication authentication) { - UsernamePasswordAuthenticationToken userToken = (UsernamePasswordAuthenticationToken)authentication; - return userToken.getName(); + return getConfiguration().getClientSecurityType() == ClientSecurityType.LDAP; } /** @@ -182,7 +205,7 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider * @return true if properties were reloaded */ private boolean reloadLdapServerProperties() { - LdapServerProperties properties = configuration.getLdapServerProperties(); + LdapServerProperties properties = getConfiguration().getLdapServerProperties(); if (!properties.equals(ldapServerProperties.get())) { LOG.info("Reloading properties"); ldapServerProperties.set(properties); @@ -194,35 +217,92 @@ public class AmbariLdapAuthenticationProvider implements AuthenticationProvider private String getLdapUserSearchFilter(String userName) { return ldapServerProperties.get() - .getUserSearchFilter(configuration.isLdapAlternateUserSearchEnabled() && AmbariLdapUtils.isUserPrincipalNameFormat(userName)); + .getUserSearchFilter(getConfiguration().isLdapAlternateUserSearchEnabled() && AmbariLdapUtils.isUserPrincipalNameFormat(userName)); } - private Integer getUserId(Authentication authentication) { - String userName = AuthorizationHelper.resolveLoginAliasToUserName(authentication.getName()); - - UserEntity userEntity = userDAO.findUserByName(userName); + /** + * Gets the {@link UserEntity} related to the authentication information + * <p> + * First the DN is retrieved from the user authentication information and a {@link UserAuthenticationEntity} + * is queried for where the type value is LDAP and key value case-insensitively matches the DN. + * If a record is found, the related {@link UserEntity} is returned. + * <p> + * Else, a {@link UserEntity} with the user name is queried. If one is found and it has a + * {@link UserAuthenticationEntity} where the type value is LDAP and key is empty, the related + * {@link UserEntity} is returned + * <p> + * Else, <code>null</code> is returned. + * + * @param authentication the user's authentication data + * @return a {@link UserEntity} + */ + private UserEntity getUserEntity(Authentication authentication) { + UserEntity userEntity = null; - // lookup is case insensitive, so no need for string comparison - if (userEntity == null) { - LOG.info("user not found ('{}')", userName); - throw new InvalidUsernamePasswordCombinationException(userName); + // Find user with the matching DN + String dn = getUserDN(authentication); + if (!StringUtils.isEmpty(dn)) { + userEntity = getUserEntityForDN(dn); } - if (!userEntity.getActive()) { - LOG.debug("User account is disabled ('{}')", userName); - } else { - List<UserAuthenticationEntity> authenticationEntities = userEntity.getAuthenticationEntities(); - for (UserAuthenticationEntity authenticationEntity : authenticationEntities) { - if (authenticationEntity.getAuthenticationType() == UserAuthenticationType.LDAP) { - // TODO: Ensure this is the "correct" LDAP entry.. - return userEntity.getUserId(); + // If a user was not found with the exact authentication properties (LDAP/dn), look up the user + // using the configured LDAP username attribute and ensure that user has an empty-keyed LDAP + // authentication entity record. + if (userEntity == null) { + String userName = AuthorizationHelper.resolveLoginAliasToUserName(authentication.getName()); + userEntity = getUsers().getUserEntity(userName); + + if (userEntity != null) { + Collection<UserAuthenticationEntity> authenticationEntities = getAuthenticationEntities(userEntity, UserAuthenticationType.LDAP); + UserEntity _userEntity = userEntity; // Hold on to the user entity value for now. + userEntity = null; // Guilty until proven innocent + + if (!CollectionUtils.isEmpty(authenticationEntities)) { + for (UserAuthenticationEntity entity : authenticationEntities) { + if (!StringUtils.isEmpty(entity.getAuthenticationKey())) { + // Proven innocent! + userEntity = _userEntity; + break; + } + } } } - - LOG.debug("Failed to find LDAP authentication entry for {})", userName); } - throw new InvalidUsernamePasswordCombinationException(userName); + return userEntity; + } + + /** + * Given a DN from the LDAP server, find the owning UserEntity. + * <p> + * DNs are case sensitive. Internally they are execpted to be stored as the bytes of the lowercase + * string. + * <p> + * DN's are expected to be unique across all {@link UserAuthenticationEntity} records for type + * UserAuthenticationType.LDAP. + * + * @param dn the DN to search for + * @return a {@link UserEntity}, if found + */ + private UserEntity getUserEntityForDN(String dn) { + Collection<UserAuthenticationEntity> authenticationEntities = getAuthenticationEntities(UserAuthenticationType.LDAP, StringUtils.lowerCase(dn)); + return ((authenticationEntities == null) || (authenticationEntities.size() != 1)) + ? null + : authenticationEntities.iterator().next().getUser(); } + /** + * Given the authentication object, attempt to retrieve the user's DN value from it. + * + * @param authentication the authentication data + * @return the relative DN; else <code>null</code> if not available + */ + private String getUserDN(Authentication authentication) { + Object objectPrincipal = (authentication == null) ? null : authentication.getPrincipal(); + if (objectPrincipal instanceof LdapUserDetails) { + return ((LdapUserDetails) objectPrincipal).getDn(); + } + + return null; + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/LdapServerProperties.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/LdapServerProperties.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/LdapServerProperties.java index a4a9516..da44ebc 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/LdapServerProperties.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/LdapServerProperties.java @@ -58,6 +58,7 @@ public class LdapServerProperties { private String groupSearchFilter; private String userSearchFilter; + private boolean alternateUserSearchFilterEnabled; private String alternateUserSearchFilter; // alternate user search filter to be used when users use their alternate login id (e.g. User Principal Name) private String syncUserMemberFilter = ""; @@ -241,6 +242,14 @@ public class LdapServerProperties { this.userSearchFilter = userSearchFilter; } + public void setAlternateUserSearchFilterEnabled(boolean alternateUserSearchFilterEnabled) { + this.alternateUserSearchFilterEnabled = alternateUserSearchFilterEnabled; + } + + public boolean isAlternateUserSearchFilterEnabled() { + return alternateUserSearchFilterEnabled; + } + public void setAlternateUserSearchFilter(String alternateUserSearchFilter) { this.alternateUserSearchFilter = alternateUserSearchFilter; } @@ -385,6 +394,7 @@ public class LdapServerProperties { if (paginationEnabled != that.isPaginationEnabled()) return false; if (userSearchFilter != null ? !userSearchFilter.equals(that.userSearchFilter) : that.userSearchFilter != null) return false; + if (alternateUserSearchFilterEnabled != that.alternateUserSearchFilterEnabled) return false; if (alternateUserSearchFilter != null ? !alternateUserSearchFilter.equals(that.alternateUserSearchFilter) : that.alternateUserSearchFilter != null) return false; if (adminGroupMappingMemberAttr != null ? !adminGroupMappingMemberAttr.equals(that.adminGroupMappingMemberAttr) : that.adminGroupMappingMemberAttr != null) return false; @@ -418,6 +428,7 @@ public class LdapServerProperties { result = 31 * result + (syncGroupMemberFilter != null ? syncGroupMemberFilter.hashCode() : 0); result = 31 * result + (referralMethod != null ? referralMethod.hashCode() : 0); result = 31 * result + (userSearchFilter != null ? userSearchFilter.hashCode() : 0); + result = 31 * result + (alternateUserSearchFilterEnabled ? 1 : 0); result = 31 * result + (alternateUserSearchFilter != null ? alternateUserSearchFilter.hashCode() : 0); result = 31 * result + (adminGroupMappingMemberAttr != null ? adminGroupMappingMemberAttr.hashCode() : 0); return result; http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/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 ebe26a2..b2a08bd 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 @@ -732,7 +732,8 @@ public class Users { String dn = user.getDn(); String authenticationKey = authenticationEntity.getAuthenticationKey(); - if (StringUtils.isEmpty(dn) || StringUtils.isEmpty(authenticationKey) || dn.equals(authenticationKey)) { + // DN's are case-insensitive. + if (StringUtils.isEmpty(dn) || StringUtils.isEmpty(authenticationKey) || dn.equalsIgnoreCase(authenticationKey)) { authenticationEntitiesToRemove.add(authenticationEntity); } iterator.remove(); @@ -768,6 +769,22 @@ public class Users { UserEntity userEntity = userDAO.findUserByName(userName); if (userEntity != null) { LOG.trace("Enabling LDAP authentication for the user account with the username {}.", userName); + + if (configuration.getLdapSyncCollisionHandlingBehavior() == Configuration.LdapUsernameCollisionHandlingBehavior.CONVERT) { + // If converting the user to only an LDAP user, then remove all other authentication methods + Collection<UserAuthenticationEntity> existingEntities = userEntity.getAuthenticationEntities(); + if(existingEntities != null) { + Iterator<UserAuthenticationEntity> iterator = existingEntities.iterator(); + while(iterator.hasNext()) { + UserAuthenticationEntity userAuthenticationEntity = iterator.next(); + if(userAuthenticationEntity.getAuthenticationType() != UserAuthenticationType.LDAP) { + removeAuthentication(userEntity, userAuthenticationEntity.getUserAuthenticationId()); + iterator.remove(); + } + } + } + } + try { addLdapAuthentication(userEntity, user.getDn(), false); userEntitiesToUpdate.add(userEntity); @@ -1122,7 +1139,32 @@ public class Users { * @return a collection of the requested {@link UserAuthenticationEntity}s */ public Collection<UserAuthenticationEntity> getUserAuthenticationEntities(String username, UserAuthenticationType authenticationType) { - if (StringUtils.isEmpty(username)) { + UserEntity userEntity; + + if (!StringUtils.isEmpty(username)) { + userEntity = userDAO.findUserByName(username); + + if (userEntity == null) { + // The requested user was not found, return null + return null; + } + } else { + // The request is for all users + userEntity = null; + } + + return getUserAuthenticationEntities(userEntity, authenticationType); + } + + /** + * Gets the collection of {@link UserAuthenticationEntity}s for a given user. + * + * @param userEntity the user; if null assumes all users + * @param authenticationType the authentication type, if null assumes all + * @return a collection of the requested {@link UserAuthenticationEntity}s + */ + public Collection<UserAuthenticationEntity> getUserAuthenticationEntities(UserEntity userEntity, UserAuthenticationType authenticationType) { + if (userEntity == null) { if (authenticationType == null) { // Get all return userAuthenticationDAO.findAll(); @@ -1131,36 +1173,32 @@ public class Users { return userAuthenticationDAO.findByType(authenticationType); } } else { - UserEntity entity = userDAO.findUserByName(username); + List<UserAuthenticationEntity> authenticationEntities = userAuthenticationDAO.findByUser(userEntity); - if (entity == null) { - return null; + if (authenticationType == null) { + // Get all for the specified user + return authenticationEntities; } else { - List<UserAuthenticationEntity> authenticationEntities = entity.getAuthenticationEntities(); - - if (authenticationType == null) { - // Get for the specified user - return authenticationEntities; - } else { - // Get for the specified user and type - List<UserAuthenticationEntity> pruned = new ArrayList<>(); - for (UserAuthenticationEntity authenticationEntity : authenticationEntities) { - if (authenticationEntity.getAuthenticationType() == authenticationType) { - pruned.add(authenticationEntity); - } + // Get for the specified user and type + List<UserAuthenticationEntity> pruned = new ArrayList<>(); + for (UserAuthenticationEntity authenticationEntity : authenticationEntities) { + if (authenticationEntity.getAuthenticationType() == authenticationType) { + pruned.add(authenticationEntity); } - - return pruned; } + + return pruned; } } } /** + * Find the {@link UserAuthenticationEntity} items for a specific {@link UserAuthenticationType} + * and key value. * - * @param authenticationType - * @param key - * @return + * @param authenticationType the authentication type + * @param key the key value + * @return the found collection of {@link UserAuthenticationEntity} values */ public Collection<UserAuthenticationEntity> getUserAuthenticationEntities(UserAuthenticationType authenticationType, String key) { return userAuthenticationDAO.findByTypeAndKey(authenticationType, key); @@ -1472,7 +1510,7 @@ public class Users { public void addLdapAuthentication(UserEntity userEntity, String dn, boolean persist) throws AmbariException { addAuthentication(userEntity, UserAuthenticationType.LDAP, - dn, + StringUtils.lowerCase(dn), // DNs are case-insensitive and are stored internally as the bytes of lowercase characters new Validator() { public void validate(UserEntity userEntity, String key) throws AmbariException { List<UserAuthenticationEntity> authenticationEntities = userEntity.getAuthenticationEntities(); @@ -1509,7 +1547,7 @@ public class Users { validator.validate(userEntity, key); - List<UserAuthenticationEntity> authenticationEntities = userEntity.getAuthenticationEntities(); + List<UserAuthenticationEntity> authenticationEntities = userAuthenticationDAO.findByUser(userEntity); UserAuthenticationEntity authenticationEntity = new UserAuthenticationEntity(); authenticationEntity.setUser(userEntity); http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java b/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java index fdc4a2f..385f22a 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java @@ -457,6 +457,9 @@ public class ConfigurationTest { ambariProperties.setProperty(Configuration.LDAP_GROUP_NAMING_ATTR.getKey(), "14"); ambariProperties.setProperty(Configuration.LDAP_ADMIN_GROUP_MAPPING_RULES.getKey(), "15"); ambariProperties.setProperty(Configuration.LDAP_GROUP_SEARCH_FILTER.getKey(), "16"); + ambariProperties.setProperty(Configuration.LDAP_USER_SEARCH_FILTER.getKey(), "17"); + ambariProperties.setProperty(Configuration.LDAP_ALT_USER_SEARCH_ENABLED.getKey(), "true"); + ambariProperties.setProperty(Configuration.LDAP_ALT_USER_SEARCH_FILTER.getKey(), "18"); final LdapServerProperties ldapProperties = configuration.getLdapServerProperties(); @@ -476,6 +479,9 @@ public class ConfigurationTest { Assert.assertEquals("14", ldapProperties.getGroupNamingAttr()); Assert.assertEquals("15", ldapProperties.getAdminGroupMappingRules()); Assert.assertEquals("16", ldapProperties.getGroupSearchFilter()); + Assert.assertEquals("17", ldapProperties.getUserSearchFilter(false)); + Assert.assertEquals(true, ldapProperties.isAlternateUserSearchFilterEnabled()); + Assert.assertEquals("18", ldapProperties.getUserSearchFilter(true)); } @Test http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthenticationTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthenticationTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthenticationTest.java deleted file mode 100644 index 64ec201..0000000 --- a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthenticationTest.java +++ /dev/null @@ -1,334 +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 junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertSame; -import static org.easymock.EasyMock.eq; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.expectLastCall; -import static org.easymock.EasyMock.verify; - -import java.security.Principal; -import java.util.Collection; -import java.util.Collections; - -import org.easymock.EasyMockRule; -import org.easymock.EasyMockSupport; -import org.easymock.Mock; -import org.easymock.MockType; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import nl.jqno.equalsverifier.EqualsVerifier; - -public class AmbariAuthenticationTest extends EasyMockSupport { - - private final Integer DEFAULT_USER_ID = 0; - - @Rule - public EasyMockRule mocks = new EasyMockRule(this); - - @Mock(type = MockType.NICE) - private ServletRequestAttributes servletRequestAttributes; - - @Mock(type = MockType.NICE) - private Authentication testAuthentication; - - @Before - public void setUp() { - resetAll(); - - RequestContextHolder.setRequestAttributes(servletRequestAttributes); - - } - - @Test - public void testGetPrincipalNoOverride() throws Exception { - // Given - Principal origPrincipal = new Principal() { - @Override - public String getName() { - return "user"; - } - }; - - Authentication authentication = new TestingAuthenticationToken(origPrincipal, "password"); - Authentication ambariAuthentication = new AmbariAuthentication(authentication, DEFAULT_USER_ID); - - // When - Object principal = ambariAuthentication.getPrincipal(); - - // Then - assertSame(origPrincipal, principal); - } - - - @Test - public void testGetPrincipal() throws Exception { - // Given - Authentication authentication = new TestingAuthenticationToken("user", "password"); - Authentication ambariAuthentication = new AmbariAuthentication(authentication, DEFAULT_USER_ID); - - // When - Object principal = ambariAuthentication.getPrincipal(); - - // Then - assertEquals("user", principal); - } - - @Test - public void testGetPrincipalWithLoginAlias() throws Exception { - // Given - Authentication authentication = new TestingAuthenticationToken("loginAlias", "password"); - expect(servletRequestAttributes.getAttribute(eq("loginAlias"), eq(RequestAttributes.SCOPE_SESSION))) - .andReturn("user").atLeastOnce(); - - replayAll(); - - Authentication ambariAuthentication = new AmbariAuthentication(authentication, DEFAULT_USER_ID); - - // When - verifyAll(); - Object principal = ambariAuthentication.getPrincipal(); - - // Then - assertEquals("user", principal); - } - - @Test - public void testGetUserDetailPrincipal() throws Exception { - // Given - UserDetails userDetails = new User("user", "password", Collections.emptyList()); - Authentication authentication = new TestingAuthenticationToken(userDetails, userDetails.getPassword()); - - Authentication ambariAuthentication = new AmbariAuthentication(authentication, DEFAULT_USER_ID); - - // When - Object principal = ambariAuthentication.getPrincipal(); - - // Then - assertEquals(userDetails, principal); - } - - @Test - public void testGetUserDetailPrincipalWithLoginAlias() throws Exception { - // Given - UserDetails userDetails = new User("loginAlias", "password", Collections.emptyList()); - Authentication authentication = new TestingAuthenticationToken(userDetails, userDetails.getPassword()); - - expect(servletRequestAttributes.getAttribute(eq("loginAlias"), eq(RequestAttributes.SCOPE_SESSION))) - .andReturn("user").atLeastOnce(); - - replayAll(); - - Authentication ambariAuthentication = new AmbariAuthentication(authentication, DEFAULT_USER_ID); - - // When - Object principal = ambariAuthentication.getPrincipal(); - - // Then - verify(); - UserDetails expectedUserDetails = new User("user", "password", Collections.emptyList()); // user detail with login alias resolved - - assertEquals(expectedUserDetails, principal); - } - - - - @Test - public void testGetNameNoOverride () throws Exception { - // Given - Principal origPrincipal = new Principal() { - @Override - public String getName() { - return "user1"; - } - }; - Authentication authentication = new TestingAuthenticationToken(origPrincipal, "password"); - Authentication ambariAuthentication = new AmbariAuthentication(authentication, DEFAULT_USER_ID); - - // When - String name = ambariAuthentication.getName(); - - // Then - assertEquals("user1", name); - } - - @Test - public void testGetName() throws Exception { - // Given - Authentication authentication = new TestingAuthenticationToken("user", "password"); - Authentication ambariAuthentication = new AmbariAuthentication(authentication, DEFAULT_USER_ID); - - // When - String name = ambariAuthentication.getName(); - - // Then - assertEquals("user", name); - } - - @Test - public void testGetNameWithLoginAlias() throws Exception { - // Given - Authentication authentication = new TestingAuthenticationToken("loginAlias", "password"); - expect(servletRequestAttributes.getAttribute(eq("loginAlias"), eq(RequestAttributes.SCOPE_SESSION))) - .andReturn("user").atLeastOnce(); - - replayAll(); - - Authentication ambariAuthentication = new AmbariAuthentication(authentication, DEFAULT_USER_ID); - - // When - String name = ambariAuthentication.getName(); - - // Then - verifyAll(); - assertEquals("user", name); - } - - @Test - public void testGetNameWithUserDetailsPrincipal() throws Exception { - // Given - UserDetails userDetails = new User("user", "password", Collections.emptyList()); - Authentication authentication = new TestingAuthenticationToken(userDetails, userDetails.getPassword()); - - Authentication ambariAuthentication = new AmbariAuthentication(authentication, DEFAULT_USER_ID); - - // When - String name = ambariAuthentication.getName(); - - // Then - assertEquals("user", name); - } - - @Test - public void testGetNameWithUserDetailsPrincipalWithLoginAlias() throws Exception { - // Given - UserDetails userDetails = new User("loginAlias", "password", Collections.emptyList()); - Authentication authentication = new TestingAuthenticationToken(userDetails, userDetails.getPassword()); - - expect(servletRequestAttributes.getAttribute(eq("loginAlias"), eq(RequestAttributes.SCOPE_SESSION))) - .andReturn("user").atLeastOnce(); - - replayAll(); - - Authentication ambariAuthentication = new AmbariAuthentication(authentication, DEFAULT_USER_ID); - - // When - String name = ambariAuthentication.getName(); - - // Then - verifyAll(); - assertEquals("user", name); - } - - @Test - public void testGetAuthorities() throws Exception { - // Given - Authentication authentication = new TestingAuthenticationToken("user", "password", "test_role"); - Authentication ambariAuthentication = new AmbariAuthentication(authentication, DEFAULT_USER_ID); - - // When - Collection<?> grantedAuthorities = ambariAuthentication.getAuthorities(); - - // Then - Collection<?> expectedAuthorities = authentication.getAuthorities(); - - assertSame(expectedAuthorities, grantedAuthorities); - } - - @Test - public void testGetCredentials() throws Exception { - // Given - String passord = "password"; - Authentication authentication = new TestingAuthenticationToken("user", passord); - Authentication ambariAuthentication = new AmbariAuthentication(authentication, DEFAULT_USER_ID); - - // When - Object credentials = ambariAuthentication.getCredentials(); - - // Then - assertSame(passord, credentials); - } - - @Test - public void testGetDetails() throws Exception { - // Given - TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "password"); - authentication.setDetails("test auth details"); - Authentication ambariAuthentication = new AmbariAuthentication(authentication, DEFAULT_USER_ID); - - // When - Object authDetails = ambariAuthentication.getDetails(); - - // Then - Object expecteAuthDetails = authentication.getDetails(); - - assertSame(expecteAuthDetails, authDetails); - } - - @Test - public void testIsAuthenticated() throws Exception { - // Given - expect(testAuthentication.isAuthenticated()).andReturn(false).once(); - - replayAll(); - - Authentication ambariAuthentication = new AmbariAuthentication(testAuthentication, DEFAULT_USER_ID); - - // When - ambariAuthentication.isAuthenticated(); - - // Then - verifyAll(); - } - - @Test - public void setTestAuthentication() throws Exception { - // Given - testAuthentication.setAuthenticated(true); - expectLastCall().once(); - - replayAll(); - - Authentication ambariAuthentication = new AmbariAuthentication(testAuthentication, DEFAULT_USER_ID); - - // When - ambariAuthentication.setAuthenticated(true); - - // Then - verifyAll(); - } - - @Test - public void testEquals() throws Exception { - EqualsVerifier.forClass(AmbariAuthentication.class) - .verify(); - } - - -} http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderForDuplicateUserTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderForDuplicateUserTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderForDuplicateUserTest.java index d465c01..e0509e2 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderForDuplicateUserTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderForDuplicateUserTest.java @@ -73,8 +73,13 @@ public class AmbariLdapAuthenticationProviderForDuplicateUserTest extends Ambari @Mock(type = MockType.NICE) private UserDAO userDAO; + @Mock(type = MockType.NICE) + private Users users; + private AmbariLdapAuthenticationProvider authenticationProvider; + private Configuration configuration; + @Before public void setUp() { Properties properties = new Properties(); @@ -87,16 +92,16 @@ public class AmbariLdapAuthenticationProviderForDuplicateUserTest extends Ambari properties.setProperty(Configuration.LDAP_BASE_DN.getKey(), "dc=apache,dc=org"); properties.setProperty(Configuration.LDAP_PRIMARY_URL.getKey(), "localhost:" + getLdapServer().getPort()); - Configuration configuration = new Configuration(properties); + configuration = new Configuration(properties); - authenticationProvider = new AmbariLdapAuthenticationProvider(configuration, authoritiesPopulator, userDAO); + authenticationProvider = new AmbariLdapAuthenticationProvider(users, authoritiesPopulator, configuration); } @Test public void testAuthenticateDuplicateUserAltUserSearchDisabled() throws Exception { // Given Authentication authentication = new UsernamePasswordAuthenticationToken("user_dup", "password"); - authenticationProvider.configuration.setProperty(Configuration.LDAP_ALT_USER_SEARCH_ENABLED.getKey(), "false"); + configuration.setProperty(Configuration.LDAP_ALT_USER_SEARCH_ENABLED.getKey(), "false"); expectedException.expect(DuplicateLdapUserFoundAuthenticationException.class); expectedException.expectMessage("Login Failed: More than one user with that username found, please work with your Ambari Administrator to adjust your LDAP configuration"); @@ -114,7 +119,7 @@ public class AmbariLdapAuthenticationProviderForDuplicateUserTest extends Ambari public void testAuthenticateDuplicateUserAltUserSearchEnabled() throws Exception { // Given Authentication authentication = new UsernamePasswordAuthenticationToken("user_dup", "password"); - authenticationProvider.configuration.setProperty(Configuration.LDAP_ALT_USER_SEARCH_ENABLED.getKey(), "true"); + configuration.setProperty(Configuration.LDAP_ALT_USER_SEARCH_ENABLED.getKey(), "true"); expectedException.expect(DuplicateLdapUserFoundAuthenticationException.class); expectedException.expectMessage("Login Failed: Please append your domain to your username and try again. Example: user_dup@domain"); http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderTest.java index 1aafaa1..43d4d6b 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariLdapAuthenticationProviderTest.java @@ -34,6 +34,7 @@ import org.apache.ambari.server.orm.GuiceJpaInitializer; import org.apache.ambari.server.orm.dao.UserDAO; import org.apache.ambari.server.orm.entities.UserEntity; import org.apache.ambari.server.security.ClientSecurityType; +import org.apache.ambari.server.security.authentication.AmbariUserAuthentication; import org.apache.ambari.server.security.authentication.InvalidUsernamePasswordCombinationException; import org.apache.directory.server.annotations.CreateLdapServer; import org.apache.directory.server.annotations.CreateTransport; @@ -88,7 +89,7 @@ public class AmbariLdapAuthenticationProviderTest extends AmbariLdapAuthenticati @Inject private Users users; @Inject - Configuration configuration; + private Configuration configuration; @Before public void setUp() { @@ -118,7 +119,7 @@ public class AmbariLdapAuthenticationProviderTest extends AmbariLdapAuthenticati AmbariLdapAuthenticationProvider provider = createMockBuilder(AmbariLdapAuthenticationProvider.class) .addMockedMethod("loadLdapAuthenticationProvider") .addMockedMethod("isLdapEnabled") - .withConstructor(configuration, authoritiesPopulator, userDAO).createMock(); + .withConstructor(users, authoritiesPopulator, configuration).createMock(); // Create the last thrown exception org.springframework.security.core.AuthenticationException exception = createNiceMock(org.springframework.security.core.AuthenticationException.class); @@ -154,7 +155,7 @@ public class AmbariLdapAuthenticationProviderTest extends AmbariLdapAuthenticati AmbariLdapAuthenticationProvider provider = createMockBuilder(AmbariLdapAuthenticationProvider.class) .addMockedMethod("loadLdapAuthenticationProvider") .addMockedMethod("isLdapEnabled") - .withConstructor(configuration, authoritiesPopulator, userDAO).createMock(); + .withConstructor(users, authoritiesPopulator, configuration).createMock(); // Create the cause org.springframework.ldap.AuthenticationException cause = createNiceMock(org.springframework.ldap.AuthenticationException.class); @@ -190,11 +191,11 @@ public class AmbariLdapAuthenticationProviderTest extends AmbariLdapAuthenticati UserEntity ldapUser = userDAO.findUserByName("allowedUser"); Authentication authentication = new UsernamePasswordAuthenticationToken("allowedUser", "password"); - AmbariAuthentication result = (AmbariAuthentication) authenticationProvider.authenticate(authentication); + AmbariUserAuthentication result = (AmbariUserAuthentication)authenticationProvider.authenticate(authentication); assertTrue(result.isAuthenticated()); assertEquals(ldapUser.getUserId(), result.getUserId()); - result = (AmbariAuthentication) authenticationProvider.authenticate(authentication); + result = (AmbariUserAuthentication) authenticationProvider.authenticate(authentication); assertTrue(result.isAuthenticated()); assertEquals(ldapUser.getUserId(), result.getUserId()); } http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AuthorizationHelperTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AuthorizationHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AuthorizationHelperTest.java index cad734c..be2b891 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AuthorizationHelperTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AuthorizationHelperTest.java @@ -186,29 +186,6 @@ public class AuthorizationHelperTest extends EasyMockSupport { } @Test - public void testLoginAliasAuthName() throws Exception { - - reset(servletRequestAttributes); - - RequestContextHolder.setRequestAttributes(servletRequestAttributes); - expect(servletRequestAttributes.getAttribute(eq("[email protected]"), eq(RequestAttributes.SCOPE_SESSION))) - .andReturn("user1").atLeastOnce(); // [email protected] is a login alias for user1 - - replay(servletRequestAttributes); - - Authentication auth = new UsernamePasswordAuthenticationToken("[email protected]", null); - SecurityContextHolder.getContext().setAuthentication(new AmbariAuthentication(auth, 0)); - - String user = AuthorizationHelper.getAuthenticatedName(); - Assert.assertEquals("user1", user); - - SecurityContextHolder.getContext().setAuthentication(null); // clean up security context - - verify(servletRequestAttributes); - - } - - @Test public void testIsAuthorized() { Provider viewInstanceDAOProvider = createNiceMock(Provider.class); http://git-wip-us.apache.org/repos/asf/ambari/blob/637c5219/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java index e99bdfd..b5a1a17 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java @@ -525,9 +525,6 @@ public class TestUsers { users.removeAuthentication(userEntity, pamAuthenticationId); assertEquals(3, users.getUserAuthenticationEntities("user", null).size()); - // UserEntity2 needs to be refreshed... - assertEquals(5, userEntity2.getAuthenticationEntities().size()); - userEntity2 = userDAO.findUserByName("user"); assertEquals(3, userEntity2.getAuthenticationEntities().size()); }
