package fr.ippon.tatami.security;
import fr.ippon.tatami.domain.User;
import fr.ippon.tatami.repository.DomainRepository;
import fr.ippon.tatami.service.UserService;
import fr.ippon.tatami.service.util.DomainUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.openid.OpenIDAttribute;
import org.springframework.security.openid.OpenIDAuthenticationToken;
import org.springframework.stereotype.Component;
import javax.inject.Inject;
import java.util.List;
/**
* UserDetails Service to be used with OpenId authentication.
* It auto-registers the user based on its "email" OpenId attribute
* (that must have been asked to the OpenId provider).
*
* @author Fabien Arrault
*/
@Component
public class OpenIdAutoRegisteringUserDetailsService implements
AuthenticationUserDetailsService<OpenIDAuthenticationToken> {
private static final String EMAIL_ATTRIBUTE = "email";
private static final String FIRSTNAME_ATTRIBUTE = "firstname";
private static final String LASTNAME_ATTRIBUTE = "lastname";
private static final String FULLNAME_ATTRIBUTE = "fullname";
private final Logger log = LoggerFactory.getLogger(OpenIdAutoRegisteringUserDetailsService.class);
@Inject
private UserService userService;
@Inject
private DomainRepository domainRepository;
@Inject
private TatamiUserDetailsService userDetailsService; // => handles grantedAuthorities
@Override
public UserDetails loadUserDetails(OpenIDAuthenticationToken token) throws UsernameNotFoundException {
String login = getAttributeValue(token, EMAIL_ATTRIBUTE);
// Important security assumption : here we are trusting the OpenID provider
// to give us an email that has already been verified to belong to the user
if (login == null) {
String msg = "OpendId response did not contain the user email";
log.error(msg);
throw new UsernameNotFoundException(msg);
}
if (!login.contains("@")) {
log.debug("User login {} from OpenId response is incorrect.", login);
throw new UsernameNotFoundException("OpendId response did not contains a valid user email");
}
// Automatically create OpenId users in Tatami :
UserDetails userDetails;
try {
userDetails = userDetailsService.loadUserByUsername(login);
// ensure that this user has access to its domain if it has been created before
domainRepository.updateUserInDomain(DomainUtil.getDomainFromLogin(login), login);
} catch (UsernameNotFoundException e) {
log.info("User with login : \"{}\" doesn't exist yet in Tatami database - creating it...", login);
userDetails = getNewlyCreatedUserDetails(token);
}
return userDetails;
}
private org.springframework.security.core.userdetails.User getNewlyCreatedUserDetails(OpenIDAuthenticationToken token) {
String login = getAttributeValue(token, EMAIL_ATTRIBUTE);
String firstName = getAttributeValue(token, FIRSTNAME_ATTRIBUTE);
String lastName = getAttributeValue(token, LASTNAME_ATTRIBUTE);
String fullName = getAttributeValue(token, FULLNAME_ATTRIBUTE);
if (firstName == null && lastName == null) {
// if we haven't first nor last name, we use fullName as last name to begin with :
lastName = fullName;
}
User user = new User();
// Note : The email could change... and the OpenId not
// moreover an OpenId account could potentially be associated with several email addresses
// so we store it for future use case :
user.setOpenIdUrl(token.getName());
user.setLogin(login);
user.setFirstName(firstName);
user.setLastName(lastName);
userService.createUser(user);
return userDetailsService.getTatamiUserDetails(login, user.getPassword());
}
private String getAttributeValue(OpenIDAuthenticationToken token, String name) {
String value = null;
for (OpenIDAttribute attribute : token.getAttributes()) {
if (name.equals(attribute.getName())) {
List<String> values = attribute.getValues();
String firstValue = values.isEmpty() ? null : values.iterator().next();
value = firstValue;
break;
}
}
return value;
}
}