· 7 years ago · Jan 24, 2018, 12:10 PM
1package com.mycompany.myapp.service;
2
3import com.mycompany.myapp.domain.Authority;
4import com.mycompany.myapp.domain.User;
5import com.mycompany.myapp.repository.AuthorityRepository;
6import com.mycompany.myapp.repository.UserRepository;
7import com.mycompany.myapp.security.AuthoritiesConstants;
8import com.mycompany.myapp.security.jwt.TokenProvider;
9import org.apache.commons.lang3.RandomStringUtils;
10import org.apache.commons.lang3.StringUtils;
11import org.slf4j.Logger;
12import org.slf4j.LoggerFactory;
13import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
14import org.springframework.security.core.context.SecurityContextHolder;
15import org.springframework.security.core.userdetails.UserDetails;
16import org.springframework.security.core.userdetails.UserDetailsService;
17import org.springframework.security.crypto.password.PasswordEncoder;
18import org.springframework.social.connect.*;
19import org.springframework.social.facebook.connect.FacebookConnectionFactory;
20import org.springframework.social.google.connect.GoogleConnectionFactory;
21import org.springframework.social.oauth1.OAuthToken;
22import org.springframework.social.oauth2.AccessGrant;
23import org.springframework.social.twitter.connect.TwitterConnectionFactory;
24import org.springframework.stereotype.Service;
25
26import java.util.*;
27
28@Service
29public class SocialService {
30
31 private final Logger log = LoggerFactory.getLogger(SocialService.class);
32
33 private final UsersConnectionRepository usersConnectionRepository;
34
35 private final AuthorityRepository authorityRepository;
36
37 private final PasswordEncoder passwordEncoder;
38
39 private final UserRepository userRepository;
40
41 private final UserDetailsService userDetailsService;
42
43 private final TokenProvider tokenProvider;
44
45 private final ConnectionFactoryLocator connectionFactoryLocator;
46
47 private final MailService mailService;
48
49 public SocialService(UsersConnectionRepository usersConnectionRepository, AuthorityRepository authorityRepository,
50 PasswordEncoder passwordEncoder, UserRepository userRepository,
51 UserDetailsService userDetailsService, TokenProvider tokenProvider, ConnectionFactoryLocator connectionFactoryLocator, MailService mailService) {
52
53 this.usersConnectionRepository = usersConnectionRepository;
54 this.authorityRepository = authorityRepository;
55 this.passwordEncoder = passwordEncoder;
56 this.userRepository = userRepository;
57 this.userDetailsService = userDetailsService;
58 this.tokenProvider = tokenProvider;
59 this.connectionFactoryLocator = connectionFactoryLocator;
60 this.mailService = mailService;
61 }
62
63 public void deleteUserSocialConnection(String login) {
64 ConnectionRepository connectionRepository = usersConnectionRepository.createConnectionRepository(login);
65 connectionRepository.findAllConnections().keySet().stream()
66 .forEach(providerId -> {
67 connectionRepository.removeConnections(providerId);
68 log.debug("Delete user social connection providerId: {}", providerId);
69 });
70 }
71
72 public User createSocialUser(Connection<?> connection, String langKey) {
73 if (connection == null) {
74 log.error("Cannot create social user because connection is null");
75 throw new IllegalArgumentException("Connection cannot be null");
76 }
77 UserProfile userProfile = connection.fetchUserProfile();
78 String providerId = connection.getKey().getProviderId();
79 String imageUrl = connection.getImageUrl();
80 User user = createUserIfNotExist(userProfile, langKey, providerId, imageUrl);
81 createSocialConnection(user.getLogin(), connection);
82 mailService.sendSocialRegistrationValidationEmail(user, providerId);
83 return user;
84 }
85
86 private User createUserIfNotExist(UserProfile userProfile, String langKey, String providerId, String imageUrl) {
87 String email = userProfile.getEmail();
88 String userName = userProfile.getUsername();
89 if (!StringUtils.isBlank(userName)) {
90 userName = userName.toLowerCase(Locale.ENGLISH);
91 }
92 if (StringUtils.isBlank(email) && StringUtils.isBlank(userName)) {
93 log.error("Cannot create social user because email and login are null");
94 throw new IllegalArgumentException("Email and login cannot be null");
95 }
96 if (StringUtils.isBlank(email) && userRepository.findOneByLogin(userName).isPresent()) {
97 log.error("Cannot create social user because email is null and login already exist, login -> {}", userName);
98 throw new IllegalArgumentException("Email cannot be null with an existing login");
99 }
100 if (!StringUtils.isBlank(email)) {
101 Optional<User> user = userRepository.findOneByEmailIgnoreCase(email);
102 if (user.isPresent()) {
103 log.info("User already exist associate the connection to this account");
104 return user.get();
105 }
106 }
107
108 String login = getLoginDependingOnProviderId(userProfile, providerId);
109 String encryptedPassword = passwordEncoder.encode(RandomStringUtils.random(10));
110 Set<Authority> authorities = new HashSet<>(1);
111 authorities.add(authorityRepository.findOne(AuthoritiesConstants.USER));
112
113 User newUser = new User();
114 newUser.setLogin(login);
115 newUser.setPassword(encryptedPassword);
116 newUser.setFirstName(userProfile.getFirstName());
117 newUser.setLastName(userProfile.getLastName());
118 newUser.setEmail(email);
119 newUser.setActivated(true);
120 newUser.setAuthorities(authorities);
121 newUser.setLangKey(langKey);
122 newUser.setImageUrl(imageUrl);
123
124 return userRepository.save(newUser);
125 }
126
127 /**
128 * @return login if provider manage a login like Twitter or GitHub otherwise email address.
129 * Because provider like Google or Facebook didn't provide login or login like "12099388847393"
130 */
131 public String getLoginDependingOnProviderId(UserProfile userProfile, String providerId) {
132 switch (providerId) {
133 case "twitter":
134 return userProfile.getUsername().toLowerCase();
135 default:
136 return userProfile.getFirstName().toLowerCase() + "_" + userProfile.getLastName().toLowerCase();
137 }
138 }
139
140 private void createSocialConnection(String login, Connection<?> connection) {
141 ConnectionRepository connectionRepository = usersConnectionRepository.createConnectionRepository(login);
142 connectionRepository.addConnection(connection);
143 }
144
145 public String loadConnectionFromToken(String token, String secret, String provider) {
146 Connection connection = null;
147 UserProfile userProfile = null;
148 if ("facebook".equals(provider)) {
149 AccessGrant accessGrant = new AccessGrant(token);
150 connection = ((FacebookConnectionFactory)connectionFactoryLocator.getConnectionFactory(provider)).createConnection(accessGrant);
151 userProfile = connection.fetchUserProfile();
152 } else if ("twitter".equals(provider)) {
153 OAuthToken oAuthToken = new OAuthToken(token, secret);
154 connection = ((TwitterConnectionFactory)connectionFactoryLocator.getConnectionFactory("twitter")).createConnection(oAuthToken);
155 userProfile = connection.fetchUserProfile();
156 } else if ("google".equals(provider)) {
157 AccessGrant accessGrant = new AccessGrant(token);
158 connection = ((GoogleConnectionFactory)connectionFactoryLocator.getConnectionFactory(provider)).createConnection(accessGrant);
159 userProfile = connection.fetchUserProfile();
160 }
161
162 // check if the user exists
163 List<String> userIds = usersConnectionRepository.findUserIdsWithConnection(connection);
164 String userLogin;
165 if (userIds.size() == 0) {
166 log.debug("User needs to be created");
167 userLogin = createSocialUser(connection, "en").getLogin();
168 } else {
169 log.debug("User already exists, logging in");
170 userLogin = getLoginDependingOnProviderId(userProfile, provider);
171
172 }
173 UserDetails user = userDetailsService.loadUserByUsername(userLogin);
174 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user,null, user.getAuthorities());
175 SecurityContextHolder.getContext().setAuthentication(authenticationToken);
176 return tokenProvider.createToken(authenticationToken, false);
177 }
178}