Skip to content

Commit

Permalink
Merge pull request #2155 from moifort/feature/social
Browse files Browse the repository at this point in the history
Add social sign in functionality (Google, Facebook, Twitter)
  • Loading branch information
jdubois committed Oct 21, 2015
2 parents c71d413 + 00bd03a commit 3f411dc
Show file tree
Hide file tree
Showing 77 changed files with 2,743 additions and 31 deletions.
103 changes: 81 additions & 22 deletions app/index.js

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions app/templates/_build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,12 @@ dependencies {
compile group: 'org.postgresql', name: 'postgresql', version: postgresql_version<% } %><% if (devDatabaseType == 'h2Disk' || devDatabaseType == 'h2Memory') { %>
compile group: 'com.h2database', name: 'h2'<% } %>
compile group: 'fr.ippon.spark.metrics', name: 'metrics-spark-reporter', version: metrics_spark_reporter_version
compile group: 'org.mapstruct', name: 'mapstruct-jdk8', version: mapstruct_version
compile group: 'org.mapstruct', name: 'mapstruct-jdk8', version: mapstruct_version<% if (enableSocialSignIn) { %>
compile group: 'org.apache.httpcomponents', name: 'httpclient', version: httpclient_version
compile group: 'org.springframework.social', name: 'spring-social-security', version: spring_social_security_version
compile group: 'org.springframework.social', name: 'spring-social-google', version: spring_social_google_version
compile group: 'org.springframework.social', name: 'spring-social-facebook', version: spring_social_facebook_version
compile group: 'org.springframework.social', name: 'spring-social-twitter', version: spring_social_twitter_version<% } %>
testCompile group: 'com.jayway.awaitility', name: 'awaitility', version: awaility_version
testCompile group: 'com.jayway.jsonpath', name: 'json-path'<% if (databaseType == 'cassandra') { %>
testCompile(group: 'org.cassandraunit', name: 'cassandra-unit-spring', version: cassandra_unit_spring_version) {
Expand All @@ -179,7 +184,6 @@ dependencies {
runtime files('lib/oracle/ojdbc/7/ojdbc-7.jar')
runtime fileTree(dir: 'lib', include: '*.jar')<% } %>
optional group: 'org.springframework.boot', name:'spring-boot-configuration-processor', version: spring_boot_version

}

compileJava.dependsOn(processResources)
Expand Down
8 changes: 7 additions & 1 deletion app/templates/_gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ mysql_connector_java_version=5.1.36<% } %>
h2_version=1.4.188
<% if (testFrameworks.indexOf('gatling') != -1) { %>
gatling_version=2.1.7<% } %>
mapstruct_version=1.0.0.CR1
mapstruct_version=1.0.0.CR1<% if (enableSocialSignIn) { %>
httpclient_version=4.5.1
spring_social_security_version=1.1.2.RELEASE
spring_social_google_version=1.0.0.RELEASE
spring_social_facebook_version=2.0.2.RELEASE
spring_social_twitter_version=1.1.1.RELEASE<% } %>


## below are some of the gradle performance improvement settings that can be used as required, these are not enabled by default

Expand Down
36 changes: 36 additions & 0 deletions app/templates/_pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@
<spring-security-oauth2.version>2.0.7.RELEASE</spring-security-oauth2.version>
<%_ } _%>
<springfox.version>2.0.3</springfox.version>
<%_ if (enableSocialSignIn) { _%>
<!-- Spring social -->
<httpclient.version>4.5.1</httpclient.version>
<spring-social-security.version>1.1.2.RELEASE</spring-social-security.version>
<spring-social-google.version>1.0.0.RELEASE</spring-social-google.version>
<spring-social-facebook.version>2.0.2.RELEASE</spring-social-facebook.version>
<spring-social-twitter.version>1.1.1.RELEASE</spring-social-twitter.version>
<%_ } _%>
<!-- Sonar properties -->
<project.testresult.directory>${project.build.directory}/test-results</project.testresult.directory>
<sonar-maven-plugin.version>2.6</sonar-maven-plugin.version>
Expand Down Expand Up @@ -543,6 +551,34 @@
<artifactId>metrics-spark-reporter</artifactId>
<version>${metrics-spark-reporter.version}</version>
</dependency>
<%_ if (enableSocialSignIn) { _%>
<!-- social -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-security</artifactId>
<version>${spring-social-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-google</artifactId>
<version>${spring-social-google.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-facebook</artifactId>
<version>${spring-social-facebook.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-twitter</artifactId>
<version>${spring-social-twitter.version}</version>
</dependency>
<%_ } _%>
<%_ if (testFrameworks.indexOf('cucumber') != -1) { _%>
<!-- cucumber -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public class JHipsterProperties {

private final Metrics metrics = new Metrics();

<%_ if (enableSocialSignIn) { _%>
private final Social social = new Social();
<%_ } _%>


public Async getAsync() {
return async;
}
Expand Down Expand Up @@ -62,6 +67,12 @@ public Metrics getMetrics() {
return metrics;
}

<%_ if (enableSocialSignIn) { _%>
public Social getSocial() {
return social;
}
<%_ } _%>

public static class Async {

private int corePoolSize = 2;
Expand Down Expand Up @@ -527,4 +538,17 @@ public void setPrefix(String prefix) {
}
}
}
<%_ if (enableSocialSignIn) { _%>
public static class Social {

private String redirectAfterSignIn = "/#/home";

public String getRedirectAfterSignIn() {
return redirectAfterSignIn;
}

public void setRedirectAfterSignIn(String redirectAfterSignIn) {
this.redirectAfterSignIn = redirectAfterSignIn;
}
}<%_ } _%>
}
6 changes: 4 additions & 2 deletions app/templates/src/main/java/package/domain/_User.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@ public class User<% if (databaseType == 'sql' || databaseType == 'mongodb') { %>
@PartitionKey
private String id;<% } %>

@NotNull
@NotNull<% if (enableSocialSignIn) { %>
@Size(min = 1, max = 100)<% if (databaseType == 'sql') { %>
@Column(length = 100, unique = true, nullable = false)<% } %><% } else { %>
@Pattern(regexp = "^[a-z0-9]*$|(anonymousUser)")
@Size(min = 1, max = 50)<% if (databaseType == 'sql') { %>
@Column(length = 50, unique = true, nullable = false)<% } %>
@Column(length = 50, unique = true, nullable = false)<% } %><% } %>
private String login;

@JsonIgnore
Expand Down
14 changes: 14 additions & 0 deletions app/templates/src/main/java/package/service/_MailService.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import org.springframework.stereotype.Service;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring4.SpringTemplateEngine;
<% if (enableSocialSignIn) { %>import org.apache.commons.lang.WordUtils;<% } %>


import javax.inject.Inject;
import javax.mail.internet.MimeMessage;
Expand Down Expand Up @@ -90,4 +92,16 @@ public void sendPasswordResetMail(User user, String baseUrl) {
String subject = messageSource.getMessage("email.reset.title", null, locale);
sendEmail(user.getEmail(), subject, content, false, true);
}
<% if (enableSocialSignIn) { %>
@Async
public void sendSocialRegistrationValidationEmail(User user, String provider) {
log.debug("Sending social registration validation e-mail to '{}'", user.getEmail());
Locale locale = Locale.forLanguageTag(user.getLangKey());
Context context = new Context(locale);
context.setVariable("user", user);
context.setVariable("provider", WordUtils.capitalize(provider));
String content = templateEngine.process("socialRegistrationValidationEmail", context);
String subject = messageSource.getMessage("email.social.registration.title", null, locale);
sendEmail(user.getEmail(), subject, content, false, true);
}<% } %>
}
44 changes: 44 additions & 0 deletions app/templates/src/main/java/package/social/_SocialController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package <%=packageName%>.social;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.social.support.URIBuilder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.view.RedirectView;

import javax.inject.Inject;

@Controller
@RequestMapping("/social")
public class SocialController {
private final Logger log = LoggerFactory.getLogger(SocialController.class);

@Inject
private SocialService socialService;

@Inject
private ProviderSignInUtils providerSignInUtils;

@RequestMapping(value = "/signup", method = RequestMethod.GET)
public RedirectView signUp(WebRequest webRequest, @CookieValue("NG_TRANSLATE_LANG_KEY") String langKey) {
try {
Connection<?> connection = providerSignInUtils.getConnectionFromSession(webRequest);
socialService.createSocialUser(connection, langKey.replace("\"", ""));
return new RedirectView(URIBuilder.fromUri("/#/social-register/" + connection.getKey().getProviderId())
.queryParam("success", "true")
.build().toString(), true);
} catch (Exception e) {
log.error("Exception creating social user: ", e);
return new RedirectView(URIBuilder.fromUri("/#/social-register/no-provider")
.queryParam("success", "false")
.build().toString(), true);
}
}

}
110 changes: 110 additions & 0 deletions app/templates/src/main/java/package/social/_SocialService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package <%=packageName%>.social;

import <%=packageName%>.domain.Authority;
import <%=packageName%>.domain.User;
import <%=packageName%>.repository.AuthorityRepository;
import <%=packageName%>.repository.UserRepository;
import <%=packageName%>.service.MailService;

import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.connect.UserProfile;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.stereotype.Service;

import javax.inject.Inject;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

@Service
public class SocialService {
private final Logger log = LoggerFactory.getLogger(SocialService.class);

@Inject
private UsersConnectionRepository usersConnectionRepository;

@Inject
private AuthorityRepository authorityRepository;

@Inject
private PasswordEncoder passwordEncoder;

@Inject
private UserRepository userRepository;

@Inject
private MailService mailService;

public void createSocialUser(Connection<?> connection, String langKey) {
if (connection == null) {
log.error("Cannot create social user because connection is null");
throw new IllegalArgumentException("Connection cannot be null");
}
UserProfile userProfile = connection.fetchUserProfile();
String providerId = connection.getKey().getProviderId();
User user = createUserIfNotExist(userProfile, langKey, providerId);
createSocialConnection(user.getLogin(), connection);
mailService.sendSocialRegistrationValidationEmail(user, providerId);
}

private User createUserIfNotExist(UserProfile userProfile, String langKey, String providerId) {
String email = userProfile.getEmail();
String userName = userProfile.getUsername();
if (StringUtils.isBlank(email) && StringUtils.isBlank(userName)) {
log.error("Cannot create social user because email and login are null");
throw new IllegalArgumentException("Email and login cannot be null");
}
if (StringUtils.isBlank(email) && userRepository.findOneByLogin(userName).isPresent()) {
log.error("Cannot create social user because email is null and login already exist, login -> {}", userName);
throw new IllegalArgumentException("Email cannot be null with an existing login");
}
Optional<User> user = userRepository.findOneByEmail(email);
if (user.isPresent()) {
log.info("User already exist associate the connection to this account");
return user.get();
}

String login = getLoginDependingOnProviderId(userProfile, providerId);
String encryptedPassword = passwordEncoder.encode(RandomStringUtils.random(10));
Set<Authority> authorities = new HashSet<>(1);
authorities.add(authorityRepository.findOne("ROLE_USER"));

User newUser = new User();
newUser.setLogin(login);
newUser.setPassword(encryptedPassword);
newUser.setFirstName(userProfile.getFirstName());
newUser.setLastName(userProfile.getLastName());
newUser.setEmail(email);
newUser.setActivated(true);
newUser.setAuthorities(authorities);
newUser.setLangKey(langKey);

return userRepository.save(newUser);
}

/**
* @param userProfile
* @param providerId
* @return login if provider manage a login like Twitter or Github otherwise email address.
* Because provider like Google or Facebook didn't provide login or login like "12099388847393"
*/
private String getLoginDependingOnProviderId(UserProfile userProfile, String providerId) {
switch (providerId) {
case "twitter":
return userProfile.getUsername();
default:
return userProfile.getEmail();
}
}

private void createSocialConnection(String login, Connection<?> connection) {
ConnectionRepository connectionRepository = usersConnectionRepository.createConnectionRepository(login);
connectionRepository.addConnection(connection);
}
}
Loading

0 comments on commit 3f411dc

Please sign in to comment.