/******************************************************************************* * Copyright (c) 2013 GigaSpaces Technologies Ltd. All rights reserved * * Licensed 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.cloudifysource.security; import java.util.logging.Logger; import org.apache.commons.lang.StringUtils; import org.springframework.context.support.MessageSourceAccessor; import org.springframework.security.authentication.AccountExpiredException; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.CredentialsExpiredException; import org.springframework.security.authentication.DisabledException; import org.springframework.security.authentication.LockedException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.SpringSecurityMessageSource; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.util.Assert; /** * An {@link AuthenticationProvider} implementation that retrieves user details * from a {@link CloudifyUserDetailsService} to supports authorization groups on * top of authorities (roles). * * @author noak * @since 2.7.0 */ public class CloudifyDaoAuthenticationProvider implements AuthenticationProvider { private final Logger logger = java.util.logging.Logger.getLogger(CloudifyDaoAuthenticationProvider.class.getName()); protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); private CloudifyUserDetailsService cloudifyUserDetailsService; /** * Verifies the user details service is set. * * @throws Exception * indicates the user details service was not set (probably not * configured probably in the xml configuration file) */ protected void doAfterPropertiesSet() throws Exception { Assert.notNull(this.cloudifyUserDetailsService, "A UserDetailsService must be set"); } public CloudifyUserDetailsService getCloudifyUserDetailsService() { return cloudifyUserDetailsService; } public void setCloudifyUserDetailsService(final CloudifyUserDetailsService cloudifyUserDetailsService) { this.cloudifyUserDetailsService = cloudifyUserDetailsService; } @Override public Authentication authenticate(final Authentication authentication) throws AuthenticationException { Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, messages.getMessage( "AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported")); logger.finest("CloudifyDaoAuthenticationProvider: authenticate"); final UsernamePasswordAuthenticationToken userToken = (UsernamePasswordAuthenticationToken) authentication; final CloudifyUserDetails user; // Determine username final String username = userToken.getName(); final String password = (String) authentication.getCredentials(); if (StringUtils.isBlank(username)) { throw new IllegalArgumentException("Empty username not allowed"); } Assert.notNull(password, "Null password was supplied in authentication token"); logger.fine("Processing authentication request for user: " + username); // Get the Cloudify user details from the user details service try { user = retrieveUser(username); String retrievedUserPassword = user.getPassword(); if (!password.equals(retrievedUserPassword)) { logger.warning("Authentication failed: password does not match stored value"); throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } } catch (final UsernameNotFoundException e) { logger.warning("User '" + username + "' not found"); throw e; } // authenticate runAuthenticationChecks(user); // create a successful and full authentication token return createSuccessfulAuthentication(userToken, user); } private CloudifyUserDetails retrieveUser(final String username) throws AuthenticationException { CloudifyUserDetails loadedUser; try { loadedUser = this.getCloudifyUserDetailsService().loadUserByUsername(username); } catch (final UsernameNotFoundException e) { throw e; } catch (final Exception repositoryProblem) { throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem); } if (loadedUser == null) { throw new AuthenticationServiceException( "CloudifyUserDetailsService returned null, which is an interface contract violation"); } return loadedUser; } /** * Creates the final <tt>Authentication</tt> object which will be returned * from the <tt>authenticate</tt> method. * * @param authentication * the original authentication request token * @param user * the <tt>UserDetails</tt> instance returned by the configured * <tt>UserDetailsContextMapper</tt>. * @return the Authentication object for the fully authenticated user. */ protected Authentication createSuccessfulAuthentication(final UsernamePasswordAuthenticationToken authentication, final CloudifyUserDetails user) { logger.finest("starting createSuccessfulAuthentication"); final CustomAuthenticationToken customAuthToken = new CustomAuthenticationToken(user, authentication.getCredentials(), user.getAuthorities(), user.getAuthGroups()); customAuthToken.setDetails(authentication.getDetails()); return customAuthToken; } private void runAuthenticationChecks(final CloudifyUserDetails user) { if (!user.isAccountNonLocked()) { logger.warning("User account is locked"); throw new LockedException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked")); } if (!user.isEnabled()) { logger.warning("User account is disabled"); throw new DisabledException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled")); } if (!user.isAccountNonExpired()) { logger.warning("User account is expired"); throw new AccountExpiredException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired")); } if (!user.isCredentialsNonExpired()) { logger.warning("User account credentials have expired"); throw new CredentialsExpiredException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.credentialsExpired", "User credentials have expired")); } } @Override public boolean supports(final Class<?> authentication) { return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); } }