/*************************************************************************
* Copyright 2009-2015 Eucalyptus Systems, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
* CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
* additional information or have any questions.
************************************************************************/
package com.eucalyptus.auth.euare.login;
import javax.security.auth.login.CredentialExpiredException;
import com.eucalyptus.auth.euare.Accounts;
import com.eucalyptus.auth.AuthException;
import com.eucalyptus.auth.AuthenticationLimitProvider;
import com.eucalyptus.auth.LdapException;
import com.eucalyptus.auth.euare.ldap.LdapSync;
import com.eucalyptus.auth.euare.principal.EuareAccount;
import com.eucalyptus.auth.euare.principal.EuareUser;
import com.eucalyptus.auth.principal.User;
import com.eucalyptus.auth.principal.UserPrincipal;
import com.eucalyptus.crypto.Crypto;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
/**
*
*/
public class PasswordAuthentication {
public static final String INVALID_USERNAME_OR_PASSWORD = "Invalid username or password";
public static final String PASSWORD_CHANGE_NOT_SUPPORTED = "Changing password is not supported for this user";
private static boolean authenticateWithLdap( final User user ) {
return LdapSync.enabled( ) && !user.isSystemAdmin( ) && !user.isAccountAdmin( );
}
/**
* Authenticates a user by {@code user}/{@code password} and if provided updates the user's
* password to be the given {@code newPassword}
*
* @param accountAlias - identified user account alias
* @param username - identified username
* @param password - user provided password
* @param newPassword - user provided new password, may be null, cannot be given w/ LDAP
* @throws AuthException
* @throws CredentialExpiredException
*/
public static UserPrincipal authenticate(
final String accountAlias,
final String username,
final String password,
final String newPassword
) throws AuthException, CredentialExpiredException {
if ( newPassword != null ) { // password change is for local region
final EuareAccount account = Accounts.lookupAccountByName( accountAlias );
final EuareUser user = account.lookupUserByName( username );
if ( authenticateWithLdap( user ) ) {
throw new AuthException( PASSWORD_CHANGE_NOT_SUPPORTED );
}
if ( !Crypto.verifyPassword(password, user.getPassword()) ) {
throw new AuthException(INVALID_USERNAME_OR_PASSWORD);
} else {
updatePassword( user, newPassword );
}
return Accounts.lookupPrincipalByUserId( user.getUserId( ) );
} else { // can be remote region if not LDAP
final String accountNumber = Accounts.lookupAccountIdByAlias( accountAlias );
final UserPrincipal user = Accounts.lookupPrincipalByAccountNumberAndUsername( accountNumber, username );
if ( authenticateWithLdap( user ) ) {
try {
LdapSync.authenticate( Accounts.lookupUserById( user.getUserId( ) ), password );
} catch ( LdapException e ) {
throw new AuthException(INVALID_USERNAME_OR_PASSWORD);
}
} else if ( !Crypto.verifyPassword( password, user.getPassword( ) ) ) {
throw new AuthException(INVALID_USERNAME_OR_PASSWORD);
} else {
checkPasswordExpiration( user );
}
return user;
}
}
private static void updatePassword( EuareUser user, String newPassword ) throws AuthException {
if ( Strings.isNullOrEmpty( newPassword ) || user.getName( ).equals( newPassword ) || newPassword.length( ) > EuareUser.MAX_PASSWORD_LENGTH ) {
throw new AuthException( AuthException.INVALID_PASSWORD );
}
String newEncrypted = Crypto.generateEncryptedPassword( newPassword );
user.setPassword( newEncrypted );
user.setPasswordExpires( System.currentTimeMillis( ) + AuthenticationLimitProvider.Values.getDefaultPasswordExpiry( ) );
}
private static void checkPasswordExpiration( final User user ) throws CredentialExpiredException {
if ( Objects.firstNonNull( user.getPasswordExpires(), Long.MAX_VALUE )
< System.currentTimeMillis() ) {
throw new CredentialExpiredException();
}
}
}