package fr.ippon.tatami.web.rest;
import com.google.api.client.auth.oauth2.TokenResponse;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.plus.Plus;
import com.google.api.services.plus.model.Person;
import com.yammer.metrics.annotation.Timed;
import fr.ippon.tatami.domain.User;
import fr.ippon.tatami.repository.DomainRepository;
import fr.ippon.tatami.security.GoogleAuthenticationToken;
import fr.ippon.tatami.security.xauth.Token;
import fr.ippon.tatami.security.xauth.TokenProvider;
import fr.ippon.tatami.service.UserService;
import fr.ippon.tatami.service.util.DomainUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import javax.inject.Inject;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
@Controller
public class UserXAuthController {
private static final Logger log = LoggerFactory.getLogger(UserXAuthController.class);
private static final String GOOGLE_AUTH_CODE_HEADER_NAME = "x-auth-code-header";
@Inject
private UserService userService;
@Inject
private TokenProvider tokenProvider;
@Inject
private UserDetailsService userDetailsService;
@Inject
private DomainRepository domainRepository;
@Inject
private AuthenticationManager authenticationManager;
@Inject
Environment env;
/**
* GET /rest/client/id -> Gets the client id
*/
@RequestMapping(value = "/rest/client/id",
method = RequestMethod.GET,
produces = "application/json")
@Timed
public Collection<String> getClientId() {
String clientId = env.getProperty("tatami.google.clientId");
Collection<String> clientList = new ArrayList<String>();
clientList.add(clientId);
return clientList;
}
/**
* POST /rest/oauth/token -> Gets a token based on the users google information
*/
@RequestMapping(value = "/rest/oauth/token",
method = RequestMethod.POST)
@Timed
public Token getGoogleUser(ServletRequest servletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String authorizationCode = httpServletRequest.getHeader(GOOGLE_AUTH_CODE_HEADER_NAME);
Token authToken = null;
if(StringUtils.hasText(authorizationCode)){
try {
Person user = getGoogleUserInfo(authorizationCode);
UserDetails userDetails = getUserDetails(user);
GoogleAuthenticationToken token = new GoogleAuthenticationToken(userDetails);
authToken = tokenProvider.createToken(userDetails);
Authentication authentication = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (IOException ioe) {
log.error("{}", ioe);
}
}
return authToken;
}
@RequestMapping(value = "/rest/authentication",
method = RequestMethod.POST)
@Timed
public Token authorize(@RequestParam String j_username, @RequestParam String j_password) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(j_username, j_password);
Authentication authentication = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
UserDetails details = userDetailsService.loadUserByUsername(j_username);
return tokenProvider.createToken(details);
}
private Person getGoogleUserInfo(String authorizationCode) throws IOException {
String clientId = env.getProperty("tatami.google.clientId");
String clientSecret = env.getProperty("tatami.google.clientSecret");
HttpTransport transport = new NetHttpTransport();
JacksonFactory jacksonFactory = new JacksonFactory();
TokenResponse accessCode = new GoogleAuthorizationCodeTokenRequest(transport, jacksonFactory,
clientId, clientSecret, authorizationCode, "http://localhost/callback")
.execute();
GoogleCredential googleCredential = new GoogleCredential.Builder()
.setJsonFactory(new JacksonFactory())
.setTransport(transport)
.setClientSecrets(clientId, clientSecret)
.build()
.setFromTokenResponse(accessCode);
Plus plus = new Plus.Builder(transport, jacksonFactory, googleCredential)
.setApplicationName("Tatami")
.build();
return plus.people().get("me").execute();
}
private UserDetails getUserDetails(Person user) throws UsernameNotFoundException {
String login = user.getEmails().get(0).getValue();
if(login == null) {
String msg = "OAuth response did not contain the user email";
log.error(msg);
throw new UsernameNotFoundException(msg);
}
if(!login.contains("@")) {
log.debug("User login {} from OAuth response is incorrect.", login);
throw new UsernameNotFoundException("OAuth response did not contains a valid user email");
}
UserDetails userDetails;
try {
userDetails = userDetailsService.loadUserByUsername(login);
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(user);
}
return userDetails;
}
private UserDetails getNewlyCreatedUserDetails(Person user) {
String login = user.getEmails().get(0).getValue();
String firstName = user.getName().getGivenName();
String lastName = user.getName().getFamilyName();
User createdUser = new User();
createdUser.setLogin(login);
createdUser.setFirstName(firstName);
createdUser.setLastName(lastName);
userService.createUser(createdUser);
return userDetailsService.loadUserByUsername(createdUser.getLogin());
}
}