/*
* Copyright (C) 2007-2014 Crafter Software Corporation.
*
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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/>.
*/
package org.craftercms.security.authentication.impl;
import org.craftercms.profile.api.Profile;
import org.craftercms.profile.api.Ticket;
import org.craftercms.profile.api.exceptions.ErrorCode;
import org.craftercms.profile.api.exceptions.ProfileException;
import org.craftercms.profile.api.services.AuthenticationService;
import org.craftercms.profile.api.services.ProfileService;
import org.craftercms.profile.exceptions.ProfileRestServiceException;
import org.craftercms.security.authentication.Authentication;
import org.craftercms.security.authentication.AuthenticationCache;
import org.craftercms.security.authentication.AuthenticationManager;
import org.craftercms.security.exception.AuthenticationException;
import org.craftercms.security.exception.AuthenticationSystemException;
import org.craftercms.security.exception.BadCredentialsException;
import org.craftercms.security.exception.DisabledUserException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
/**
* Default implementation of {@link org.craftercms.security.authentication.AuthenticationManager}.
*
* @author avasquez
*/
public class AuthenticationManagerImpl implements AuthenticationManager {
private static final Logger logger = LoggerFactory.getLogger(AuthenticationManagerImpl.class);
protected AuthenticationService authenticationService;
protected ProfileService profileService;
protected AuthenticationCache authenticationCache;
@Required
public void setAuthenticationService(AuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
@Required
public void setProfileService(ProfileService profileService) {
this.profileService = profileService;
}
@Required
public void setAuthenticationCache(AuthenticationCache authenticationCache) {
this.authenticationCache = authenticationCache;
}
@Override
public Authentication authenticateUser(String tenant, String username, String password) {
try {
Ticket ticket = authenticationService.authenticate(tenant, username, password);
Profile profile = profileService.getProfile(ticket.getProfileId());
if (profile == null) {
throw new AuthenticationSystemException("No profile found for ID '" + ticket.getProfileId() + "'");
}
String ticketId = ticket.getId();
DefaultAuthentication auth = new DefaultAuthentication(ticketId, profile);
authenticationCache.putAuthentication(auth);
logger.debug("Authentication successful for user '{}' (ticket ID = '{}')", ticket.getProfileId(), ticketId);
return auth;
} catch (ProfileRestServiceException e) {
switch (e.getErrorCode()) {
case DISABLED_PROFILE:
throw new DisabledUserException("User is disabled", e);
case BAD_CREDENTIALS:
throw new BadCredentialsException("Invalid username and/or password", e);
default:
throw new AuthenticationSystemException("An unexpected error occurred while authenticating", e);
}
} catch (ProfileException e) {
throw new AuthenticationSystemException("An unexpected error occurred while authenticating", e);
}
}
@Override
public Authentication authenticateUser(String[] tenants, String username,
String password) throws AuthenticationException {
for (String tenant : tenants) {
try {
return authenticateUser(tenant, username, password);
} catch (BadCredentialsException e) {
if (logger.isDebugEnabled()) {
logger.debug("Authentication attempt for user '" + username + "' with tenant '" + tenant +
"' failed. Trying with next tenant...", e);
}
}
}
throw new BadCredentialsException("Invalid username and/or password");
}
@Override
public Authentication authenticateUser(Profile profile) throws AuthenticationException {
return authenticateUser(profile, false);
}
@Override
public Authentication authenticateUser(Profile profile, boolean remembered) throws AuthenticationException {
try {
Ticket ticket = authenticationService.createTicket(profile.getId().toString());
String ticketId = ticket.getId();
DefaultAuthentication auth = new DefaultAuthentication(ticketId, profile, remembered);
authenticationCache.putAuthentication(auth);
logger.debug("Authentication successful for user '{}' (ticket ID = '{}')", ticket.getProfileId(), ticketId);
return auth;
} catch (ProfileRestServiceException e) {
if (e.getErrorCode() == ErrorCode.DISABLED_PROFILE) {
throw new DisabledUserException("User is disabled", e);
} else {
throw new AuthenticationSystemException("An unexpected error occurred while authenticating", e);
}
} catch (ProfileException e) {
throw new AuthenticationSystemException("An unexpected error occurred while authenticating", e);
}
}
@Override
public Authentication getAuthentication(String ticket, boolean reloadProfile) throws AuthenticationException {
Authentication auth = null;
if (!reloadProfile) {
auth = authenticationCache.getAuthentication(ticket);
}
if (auth == null) {
if (reloadProfile) {
logger.debug("Profile reload forced for ticket '{}'", ticket);
} else {
logger.debug("Ticket '{}' found in request but there's no cached authentication for it", ticket);
}
Profile profile = loadProfile(ticket);
if (profile != null) {
auth = new DefaultAuthentication(ticket, profile);
authenticationCache.putAuthentication(auth);
} else {
return null;
}
}
return auth;
}
@Override
public void invalidateAuthentication(Authentication authentication) {
try {
authenticationCache.removeAuthentication(authentication.getTicket());
authenticationService.invalidateTicket(authentication.getTicket());
logger.debug("Ticket '{}' successfully invalidated");
} catch (ProfileException e) {
throw new AuthenticationSystemException("An unexpected error occurred while attempting to invalidate " +
"ticket '" + authentication.getTicket() + "'", e);
}
}
protected Profile loadProfile(String ticketId) throws AuthenticationException {
try {
Profile profile = profileService.getProfileByTicket(ticketId);
if (profile != null) {
logger.debug("Profile '{}' retrieved for ticket '{}'", profile.getId(), ticketId);
return profile;
} else {
throw new AuthenticationSystemException("No profile found for ticket '" + ticketId + "'");
}
} catch (ProfileRestServiceException e) {
if (e.getErrorCode() == ErrorCode.NO_SUCH_TICKET) {
logger.debug("Ticket '{}' is invalid", ticketId);
return null;
} else {
throw new AuthenticationSystemException("An unexpected error occurred while attempting to retrieve " +
"profile for ticket '" + ticketId + "'", e);
}
} catch (ProfileException e) {
throw new AuthenticationSystemException("An unexpected error occurred while attempting to retrieve " +
"profile for ticket '" + ticketId + "'", e);
}
}
}