Commit d56b408d authored by Matija Obreza's avatar Matija Obreza

Merge branch '395-google-sign-in-authentication-system' into 'master'

Resolve "Google Sign-in authentication system"

Closes #395

See merge request genesys-pgr/genesys-server!319
parents c1c755f1 0c73b57c
...@@ -76,7 +76,6 @@ ...@@ -76,7 +76,6 @@
<spring.security.oauth2.version>2.3.3.RELEASE</spring.security.oauth2.version> <spring.security.oauth2.version>2.3.3.RELEASE</spring.security.oauth2.version>
<spring-security-jwt>1.0.8.RELEASE</spring-security-jwt> <spring-security-jwt>1.0.8.RELEASE</spring-security-jwt>
<org.springframework.social-version>1.1.4.RELEASE</org.springframework.social-version> <org.springframework.social-version>1.1.4.RELEASE</org.springframework.social-version>
<org.springframework.social-google-version>1.0.0.RELEASE</org.springframework.social-google-version>
<querydsl.version>4.1.4</querydsl.version> <querydsl.version>4.1.4</querydsl.version>
<hibernate.version>4.3.11.Final</hibernate.version> <hibernate.version>4.3.11.Final</hibernate.version>
...@@ -560,23 +559,16 @@ ...@@ -560,23 +559,16 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.social</groupId> <groupId>com.google.api-client</groupId>
<artifactId>spring-social-google</artifactId> <artifactId>google-api-client</artifactId>
<version>${org.springframework.social-google-version}</version> <version>1.27.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.social</groupId> <groupId>org.springframework.social</groupId>
<artifactId>spring-social-web</artifactId> <artifactId>spring-social-web</artifactId>
<version>${org.springframework.social-version}</version> <version>${org.springframework.social-version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-core</artifactId>
<version>${org.springframework.social-version}</version>
</dependency>
<!--Test dependencies --> <!--Test dependencies -->
<dependency> <dependency>
<groupId>org.hamcrest</groupId> <groupId>org.hamcrest</groupId>
......
...@@ -254,7 +254,7 @@ public class DatasetServiceImpl implements DatasetService { ...@@ -254,7 +254,7 @@ public class DatasetServiceImpl implements DatasetService {
} }
long deleted = accessionRefRepository.deleteForDataset(dataset); long deleted = accessionRefRepository.deleteForDataset(dataset);
System.err.println("Deleted " + deleted + " refs for dataset"); LOG.trace("Deleted {} refs for dataset", deleted);
return addAccessionRefs(dataset, accessionRefs); return addAccessionRefs(dataset, accessionRefs);
} }
...@@ -614,33 +614,27 @@ public class DatasetServiceImpl implements DatasetService { ...@@ -614,33 +614,27 @@ public class DatasetServiceImpl implements DatasetService {
accessionRefRepository.save(dArs); accessionRefRepository.save(dArs);
loadedDataset.setAccessionCount((int) accessionRefRepository.countByDataset(loadedDataset)); loadedDataset.setAccessionCount((int) accessionRefRepository.countByDataset(loadedDataset));
LOG.warn("Done saving {} accession refs, have {} in dataset", accessionRefs.size(), loadedDataset.getAccessionCount()); LOG.warn("Done saving {} accession refs, have {} in dataset", accessionRefs.size(), loadedDataset.getAccessionCount());
datasetRepository.save(loadedDataset);
threadPoolTaskExecutor.execute(() -> {
try {
Thread.sleep(2000);
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can only be done
// programmatically
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(def); threadPoolTaskExecutor.execute(() -> {
try { DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// execute your business logic here // explicitly setting the transaction name is something that can only be done
rematchDatasetAccessions(dArs); // programmatically
} catch (Throwable ex) { def.setName("SomeTxName");
LOG.error("Rolling back rematch. Exception: {}", ex.getMessage(), ex); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
transactionManager.rollback(status);
throw ex;
}
transactionManager.commit(status);
} catch (InterruptedException e) { TransactionStatus status = transactionManager.getTransaction(def);
LOG.warn("Interrupted rematcher"); try {
// execute your business logic here
rematchDatasetAccessions(dArs);
} catch (Throwable ex) {
LOG.error("Rolling back rematch. Exception: {}", ex.getMessage(), ex);
transactionManager.rollback(status);
throw ex;
} }
transactionManager.commit(status);
}); });
return lazyLoad(datasetRepository.save(loadedDataset)); return lazyLoad(loadedDataset);
} }
/** /**
......
/** /*
* Copyright 2014 Global Crop Diversity Trust * Copyright 2018 Global Crop Diversity Trust
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -12,28 +12,17 @@ ...@@ -12,28 +12,17 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
**/ */
package org.genesys2.server.component.security; package org.genesys2.server.component.security;
import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.util.Arrays;
import java.util.ArrayList; import java.util.Collections;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.genesys.blocks.security.UserException; import org.genesys.blocks.security.UserException;
import org.genesys.blocks.security.model.BasicUser; import org.genesys.blocks.security.model.BasicUser;
import org.genesys2.server.model.impl.User; import org.genesys2.server.model.impl.User;
...@@ -41,26 +30,27 @@ import org.genesys2.server.service.UserService; ...@@ -41,26 +30,27 @@ import org.genesys2.server.service.UserService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; 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.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.social.google.api.plus.Person;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.JsonNode; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl;
import com.fasterxml.jackson.databind.ObjectMapper; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
@Component @Component
public class GoogleOAuthUtil { public class GoogleOAuthUtil {
private static final Logger LOG = LoggerFactory.getLogger(GoogleOAuthUtil.class); private static final Logger LOG = LoggerFactory.getLogger(GoogleOAuthUtil.class);
public static final String LOCAL_GOOGLEAUTH_PATH = "/google/auth"; public static final String LOCAL_GOOGLEAUTH_PATH = "/google/auth";
private ObjectMapper mapper = new ObjectMapper();
@Value("${base.url}") @Value("${base.url}")
private String baseUrl; private String baseUrl;
...@@ -74,84 +64,50 @@ public class GoogleOAuthUtil { ...@@ -74,84 +64,50 @@ public class GoogleOAuthUtil {
private UserService userService; private UserService userService;
@Autowired @Autowired
@Named("authUserDetailsService") @Qualifier("authUserDetailsService")
private UserDetailsService userDetailsService; private UserDetailsService userDetailsService;
public String exchangeForAccessToken(HttpServletRequest request) throws IOException { public GoogleTokenResponse exchangeForAccessToken(HttpServletRequest request) throws IOException {
final CloseableHttpClient httpclient = HttpClientBuilder.create().build(); // Exchange auth code for access token
return new GoogleAuthorizationCodeTokenRequest(
try { new NetHttpTransport(),
final HttpPost httppost = new HttpPost("https://accounts.google.com/o/oauth2/token"); JacksonFactory.getDefaultInstance(),
"https://www.googleapis.com/oauth2/v4/token",
final List<NameValuePair> params = new ArrayList<>(); googleApiClientId,
params.add(new BasicNameValuePair("code", request.getParameter("code"))); secret,
params.add(new BasicNameValuePair("client_id", googleApiClientId)); request.getParameter("code"),
params.add(new BasicNameValuePair("client_secret", secret)); baseUrl + LOCAL_GOOGLEAUTH_PATH).execute();
params.add(new BasicNameValuePair("redirect_uri", baseUrl + LOCAL_GOOGLEAUTH_PATH));
params.add(new BasicNameValuePair("grant_type", "authorization_code"));
params.add(new BasicNameValuePair("scope", ""));
httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
final HttpResponse response = httpclient.execute(httppost);
final BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
final StringBuilder builder = new StringBuilder();
for (String line = null; (line = reader.readLine()) != null;) {
builder.append(line).append("\n");
}
JsonNode json = mapper.readTree(builder.toString());
return json.has("access_token") ? json.get("access_token").textValue() : null;
} finally {
httpclient.close();
}
} }
public String getAuthenticationUrl() { public String getAuthenticationUrl() {
// google.auth.url=https://accounts.google.com/o/oauth2/auth?redirect_uri=http://localhost:8080/g/auth&response_type=code&client_id=12345-hfp8qjfeqaefpitbc707uluuh8vq65k7.apps.googleusercontent.com&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fplus.login+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fplus.me+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&approval_prompt=auto&access_type=online&include_granted_scopes=true return new GoogleAuthorizationCodeRequestUrl(googleApiClientId, baseUrl + LOCAL_GOOGLEAUTH_PATH,
final List<NameValuePair> parameters = new ArrayList<NameValuePair>(); Arrays.asList("https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"))
parameters.add(new BasicNameValuePair("redirect_uri", baseUrl + LOCAL_GOOGLEAUTH_PATH)); .setResponseTypes(Collections.singletonList("code"))
parameters.add(new BasicNameValuePair("response_type", "code")); .setApprovalPrompt("auto").build();
parameters.add(new BasicNameValuePair("client_id", googleApiClientId));
parameters.add(new BasicNameValuePair("approval_prompt", "auto"));
parameters.add(new BasicNameValuePair("access_type", "online"));
parameters.add(new BasicNameValuePair("include_granted_scopes", "true"));
// Google+
// "https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"));
// Only basic:
parameters.add(new BasicNameValuePair("scope", "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email"));
final String query = URLEncodedUtils.format(parameters, "UTF-8");
return "https://accounts.google.com/o/oauth2/auth?" + query;
} }
public Authentication googleAuthentication(Person userInfo) { public Authentication googleAuthentication(GoogleIdToken.Payload tokenPayload) {
try { try {
final UserDetails userDetails = userDetailsService.loadUserByUsername(userInfo.getAccountEmail()); final UserDetails userDetails = userDetailsService.loadUserByUsername(tokenPayload.getEmail());
if (!(userDetails.isEnabled() && userDetails.isAccountNonExpired() && userDetails.isAccountNonLocked() && userDetails.isCredentialsNonExpired())) { if (!(userDetails.isEnabled() && userDetails.isAccountNonExpired() && userDetails.isAccountNonLocked() && userDetails.isCredentialsNonExpired())) {
LOG.warn("Google login canceled: Account currently not available: {}", userInfo.getAccountEmail()); LOG.warn("Google login canceled: Account currently not available: {}", tokenPayload.getEmail());
return null; return null;
} }
final Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); final Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
return authentication; return authentication;
} catch (final UsernameNotFoundException e) { } catch (final UsernameNotFoundException e) {
LOG.warn("Authentication with Google+ failed: No such user {}", userInfo.getAccountEmail()); LOG.warn("Authentication with Google failed: No such user {}", tokenPayload.getEmail());
return null; return null;
} }
} }
public User extractUserFromGoogleProfile(Person userInfo) throws UserException { public User extractUserFromGoogleTokenPayload(GoogleIdToken.Payload tokenPayload) throws UserException {
User user = null; User user = null;
try { try {
user = userService.getUserByEmail(userInfo.getAccountEmail()); user = (User) userDetailsService.loadUserByUsername(tokenPayload.getEmail());
if(user == null){
throw new UsernameNotFoundException("User not found");
}
if (user.getAccountType() == BasicUser.AccountType.LOCAL) { if (user.getAccountType() == BasicUser.AccountType.LOCAL) {
// account exists, change to {@link LoginType#GOOGLE} // account exists, change to {@link LoginType#GOOGLE}
LOG.info("Changing account type to LoginType#GOOGLE"); LOG.info("Changing account type to LoginType#GOOGLE");
...@@ -159,7 +115,7 @@ public class GoogleOAuthUtil { ...@@ -159,7 +115,7 @@ public class GoogleOAuthUtil {
} }
} catch (UsernameNotFoundException e) { } catch (UsernameNotFoundException e) {
LOG.info("Username not found, creating new Google account"); LOG.info("Username not found, creating new Google account");
user = userService.createUser(userInfo.getAccountEmail(), userInfo.getDisplayName(),null, BasicUser.AccountType.GOOGLE); user = userService.createUser(tokenPayload.getEmail(), (String) tokenPayload.get("name"), null, BasicUser.AccountType.GOOGLE);
userService.userEmailValidated(UUID.fromString(user.getUuid())); userService.userEmailValidated(UUID.fromString(user.getUuid()));
} }
return user; return user;
......
/** /*
* Copyright 2014 Global Crop Diversity Trust * Copyright 2018 Global Crop Diversity Trust
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -12,12 +12,14 @@ ...@@ -12,12 +12,14 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
**/ */
package org.genesys2.server.mvc; package org.genesys2.server.mvc;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
...@@ -25,15 +27,18 @@ import javax.servlet.ServletException; ...@@ -25,15 +27,18 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.genesys.blocks.security.NotUniqueUserException;
import org.genesys.blocks.security.UserException; import org.genesys.blocks.security.UserException;
import org.genesys.blocks.security.service.PasswordPolicy.PasswordPolicyException;
import org.genesys2.server.component.security.GoogleOAuthUtil; import org.genesys2.server.component.security.GoogleOAuthUtil;
import org.genesys2.server.model.impl.User; import org.genesys2.server.model.impl.User;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
...@@ -41,9 +46,6 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand ...@@ -41,9 +46,6 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand
import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.social.google.api.Google;
import org.springframework.social.google.api.impl.GoogleTemplate;
import org.springframework.social.google.api.plus.Person;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
...@@ -51,6 +53,13 @@ import org.springframework.web.bind.annotation.RequestMethod; ...@@ -51,6 +53,13 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
@Controller @Controller
public class GoogleSocialController extends BaseController { public class GoogleSocialController extends BaseController {
...@@ -63,66 +72,81 @@ public class GoogleSocialController extends BaseController { ...@@ -63,66 +72,81 @@ public class GoogleSocialController extends BaseController {
@Autowired @Autowired
private AuthorizationServerTokenServices tokenServices; private AuthorizationServerTokenServices tokenServices;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Value("${google.consumerKey}")
private String googleApiClientId;
@RequestMapping("/google/login") @RequestMapping("/google/login")
public void redirectToGoogle(HttpServletResponse response) throws IOException { public void redirectToGoogle(HttpServletResponse response) throws IOException {
response.sendRedirect(googleOAuthUtil.getAuthenticationUrl()); response.sendRedirect(googleOAuthUtil.getAuthenticationUrl());
} }
/** /**
* @throws PasswordPolicyException Shouldn't happen * @throws UsernameNotFoundException
* @throws NotUniqueUserException
*/ */
@RequestMapping(GoogleOAuthUtil.LOCAL_GOOGLEAUTH_PATH) @RequestMapping(GoogleOAuthUtil.LOCAL_GOOGLEAUTH_PATH)
public void googleAuth(Model model, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, UserException { public void googleAuth(Model model, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String accessToken = null; GoogleTokenResponse googleTokenResponse = googleOAuthUtil.exchangeForAccessToken(request);
try {
accessToken = googleOAuthUtil.exchangeForAccessToken(request);
} catch (IOException e) {
LOG.warn(e.getMessage(), e);
}
if (accessToken == null) { if (googleTokenResponse == null) {
model.addAttribute("error", true); model.addAttribute("error", true);
authFailureHandler.onAuthenticationFailure(request, response, new BadCredentialsException("Could not authenticate you with Google+")); authFailureHandler.onAuthenticationFailure(request, response, new BadCredentialsException("Could not authenticate you with Google"));
return; return;
} }
final Google google = new GoogleTemplate(accessToken); // Get profile info from ID token
final Person userInfo = google.plusOperations().getGoogleProfile(); GoogleIdToken idToken = googleTokenResponse.parseIdToken();
try {
googleOAuthUtil.extractUserFromGoogleProfile(userInfo); User user = googleOAuthUtil.extractUserFromGoogleTokenPayload(idToken.getPayload());
LOG.warn("Google auth for {}", user.getEmail());
final Authentication authentication = googleOAuthUtil.googleAuthentication(userInfo); final Authentication authentication = googleOAuthUtil.googleAuthentication(idToken.getPayload());
SecurityContextHolder.getContext().setAuthentication(authentication);
// Redirect to URL in session
authSuccessHandler.onAuthenticationSuccess(request, response, authentication); applicationEventPublisher.publishEvent(new AuthenticationSuccessEvent(authentication));
// Redirect to URL in session
authSuccessHandler.onAuthenticationSuccess(request, response, authentication);
} catch (UserException e) {
LOG.error(e.getMessage(), e);
}
} }
/** /**
* Google XHR auth. * Google XHR auth.
* *
* @param accessToken the access token * @param tokenId the user's ID token
* @param clientId the client id * @param clientId the client id
* @return the object * @return the object
*/ */
@RequestMapping(value = "/google/verify-token", method = RequestMethod.GET) @RequestMapping(value = "/google/verify-token", method = RequestMethod.GET)
@ResponseBody @ResponseBody
public Object googleAuth(@RequestParam("accessToken") final String accessToken, public Object googleAuth(@RequestParam("tokenId") final String tokenId,
@RequestParam("clientId") final String clientId) throws UserException { @RequestParam("clientId") final String clientId) throws UserException, IOException, GeneralSecurityException {
final Google google = new GoogleTemplate(accessToken); GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance())
final Person userInfo = google.plusOperations().getGoogleProfile(); .setAudience(Collections.singletonList(googleApiClientId)).build();
GoogleIdToken googleIdToken = verifier.verify(tokenId);
User user = googleOAuthUtil.extractUserFromGoogleProfile(userInfo);
if (googleIdToken != null) {
final Set<String> scope = new HashSet<>(Arrays.asList("trust", "read", "write")); Payload payload = googleIdToken.getPayload();
final OAuth2Request oAuth2Request = new OAuth2Request(null, clientId, user.getAuthorities(), true, scope, null, null, null, null); User user = googleOAuthUtil.extractUserFromGoogleTokenPayload(payload);
final UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); final Set<String> scope = new HashSet<>(Arrays.asList("trust", "read", "write"));
final OAuth2Authentication auth = new OAuth2Authentication(oAuth2Request, authenticationToken); final OAuth2Request oAuth2Request = new OAuth2Request(null, clientId, user.getAuthorities(), true, scope, null, null, null, null);
return tokenServices.createAccessToken(auth); final UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
final OAuth2Authentication authentication = new OAuth2Authentication(oAuth2Request, authenticationToken);
applicationEventPublisher.publishEvent(new AuthenticationSuccessEvent(authentication));
return tokenServices.createAccessToken(authentication);
} else {
throw new BadCredentialsException("Could not authenticate you with Google");
}
} }
} }
...@@ -366,33 +366,28 @@ public class SubsetServiceImpl implements SubsetService { ...@@ -366,33 +366,28 @@ public class SubsetServiceImpl implements SubsetService {
accessionRefRepository.save(sArs); accessionRefRepository.save(sArs);
loadedSubset.setAccessionCount((int) accessionRefRepository.countBySubset(loadedSubset)); loadedSubset.setAccessionCount((int) accessionRefRepository.countBySubset(loadedSubset));
LOG.warn("Done saving {} accession refs, have {} in subset", accessionRefs.size(), loadedSubset.getAccessionCount()); LOG.warn("Done saving {} accession refs, have {} in subset", accessionRefs.size(), loadedSubset.getAccessionCount());
subsetRepository.save(loadedSubset);
threadPoolTaskExecutor.execute(() -> { threadPoolTaskExecutor.execute(() -> {
try { DefaultTransactionDefinition def = new DefaultTransactionDefinition();
Thread.sleep(2000); // explicitly setting the transaction name is something that can only be done
// programmatically
DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setName("SomeTxName");
// explicitly setting the transaction name is something that can only be done def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// programmatically
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(def);
try {
// execute your business logic here
rematchSubsetAccessions(sArs);
} catch (Throwable ex) {
LOG.error("Rolling back rematch. Exception: {}", ex.getMessage(), ex);
transactionManager.rollback(status);
throw ex;
}
transactionManager.commit(status);
} catch (InterruptedException e) { TransactionStatus status = transactionManager.getTransaction(def);
LOG.warn("Interrupted rematcher"); try {
// execute your business logic here
rematchSubsetAccessions(sArs);
} catch (Throwable ex) {
LOG.error("Rolling back rematch. Exception: {}", ex.getMessage(), ex);
transactionManager.rollback(status);
throw ex;
} }
transactionManager.commit(status);
}); });
return lazyLoad(subsetRepository.save(loadedSubset));
return lazyLoad(loadedSubset);
} }
/** /**
......
...@@ -41,6 +41,7 @@ login.register-now=Create an account ...@@ -41,6 +41,7 @@ login.register-now=Create an account
logout=Logout logout=Logout
login.forgot-password=Forgot password login.forgot-password=Forgot password
login.with-google-plus=Login with Google+ login.with-google-plus=Login with Google+
login.with-google-sign-in=Login with Google
# Registration # Registration
registration.page.title=Create a user account registration.page.title=Create a user account
......
...@@ -167,7 +167,7 @@ ...@@ -167,7 +167,7 @@
<spring:message code="login.login-button" /> <spring:message code="login.login-button" />
</button> </button>
<span class="or">-</span> <span class="or">-</span>
<a href="<c:url value="/google/login" />" class="btn btn-default google-signin"> <spring:message code="login.with-google-plus" /> <a href="<c:url value="/google/login" />" class="btn btn-default google-signin"> <spring:message code="login.with-google-sign-in" />
</a> <a href="<c:url value="/registration" />" class="btn btn-default"> <spring:message code="login.register-now" /> </a> <a href="<c:url value="/registration" />" class="btn btn-default"> <spring:message code="login.register-now" />
</a>