Implements login provider LinkedIn. (Issue #163)

This commit is contained in:
gkolokythas 2019-09-30 10:27:42 +03:00
parent 85c9022a90
commit 09d4241df5
15 changed files with 236 additions and 30 deletions

View File

@ -7,6 +7,9 @@ import eu.eudat.logic.security.CustomAuthenticationProvider;
import eu.eudat.logic.security.validators.b2access.B2AccessTokenValidator;
import eu.eudat.logic.security.validators.b2access.helpers.B2AccessRequest;
import eu.eudat.logic.security.validators.b2access.helpers.B2AccessResponseToken;
import eu.eudat.logic.security.validators.linkedin.LinkedInTokenValidator;
import eu.eudat.logic.security.validators.linkedin.helpers.LinkedInRequest;
import eu.eudat.logic.security.validators.linkedin.helpers.LinkedInResponseToken;
import eu.eudat.logic.security.validators.orcid.ORCIDTokenValidator;
import eu.eudat.logic.security.validators.orcid.helpers.ORCIDRequest;
import eu.eudat.logic.security.validators.orcid.helpers.ORCIDResponseToken;
@ -37,17 +40,19 @@ public class Login {
private TwitterTokenValidator twitterTokenValidator;
private B2AccessTokenValidator b2AccessTokenValidator;
private ORCIDTokenValidator orcidTokenValidator;
private LinkedInTokenValidator linkedInTokenValidator;
private Logger logger;
private UserManager userManager;
@Autowired
public Login(CustomAuthenticationProvider customAuthenticationProvider, AuthenticationService nonVerifiedUserAuthenticationService,
TwitterTokenValidator twitterTokenValidator, B2AccessTokenValidator b2AccessTokenValidator, ORCIDTokenValidator orcidTokenValidator,
UserManager userManager ,Logger logger) {
TwitterTokenValidator twitterTokenValidator, LinkedInTokenValidator linkedInTokenValidator, B2AccessTokenValidator b2AccessTokenValidator,
ORCIDTokenValidator orcidTokenValidator, UserManager userManager, Logger logger) {
this.customAuthenticationProvider = customAuthenticationProvider;
this.nonVerifiedUserAuthenticationService = nonVerifiedUserAuthenticationService;
this.twitterTokenValidator = twitterTokenValidator;
this.linkedInTokenValidator = linkedInTokenValidator;
this.b2AccessTokenValidator = b2AccessTokenValidator;
this.orcidTokenValidator = orcidTokenValidator;
this.logger = logger;
@ -76,6 +81,12 @@ public class Login {
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<OAuthToken>().payload(this.twitterTokenValidator.getRequestToken()).status(ApiMessageCode.NO_MESSAGE));
}
@RequestMapping(method = RequestMethod.POST, value = {"/linkedInRequestToken"}, produces = "application/json", consumes = "application/json")
public @ResponseBody
ResponseEntity<ResponseItem<LinkedInResponseToken>> linkedInRequestToken(@RequestBody LinkedInRequest linkedInRequest) {
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<LinkedInResponseToken>().payload(this.linkedInTokenValidator.getAccessToken(linkedInRequest)).status(ApiMessageCode.NO_MESSAGE));
}
@RequestMapping(method = RequestMethod.POST, value = {"/b2AccessRequestToken"}, produces = "application/json", consumes = "application/json")
public @ResponseBody
ResponseEntity<ResponseItem<B2AccessResponseToken>> b2AccessRequestToken(@RequestBody B2AccessRequest b2AccessRequest) {

View File

@ -0,0 +1,10 @@
package eu.eudat.logic.security.customproviders.LinkedIn;
import eu.eudat.logic.security.validators.linkedin.helpers.LinkedInResponseToken;
public interface LinkedInCustomProvider {
LinkedInUser getUser(String accessToken);
LinkedInResponseToken getAccessToken(String code, String redirectUri, String clientId, String clientSecret);
}

View File

@ -0,0 +1,71 @@
package eu.eudat.logic.security.customproviders.LinkedIn;
import eu.eudat.logic.security.validators.linkedin.helpers.LinkedInResponseToken;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
@Component("LinkedInCustomProvider")
public class LinkedInCustomProviderImpl implements LinkedInCustomProvider {
private Environment environment;
public LinkedInCustomProviderImpl(Environment environment) {
this.environment = environment;
}
public LinkedInUser getUser(String accessToken) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = this.createBearerAuthHeaders(accessToken);
HttpEntity<String> entity = new HttpEntity<>(headers);
Map profileValues = restTemplate.exchange(this.environment.getProperty("linkedin.login.user_info_url"), HttpMethod.GET, entity, Map.class).getBody();
Map emailValues = restTemplate.exchange(this.environment.getProperty("linkedin.login.user_email"), HttpMethod.GET, entity, Map.class).getBody();
LinkedInUser linkedInUser = new LinkedInUser();
linkedInUser.setEmail((String)emailValues.get("email"));
linkedInUser.setName((String)profileValues.get("localizedFirstName"));
linkedInUser.setId((String)profileValues.get("id"));
return linkedInUser;
}
public LinkedInResponseToken getAccessToken(String code, String redirectUri, String clientId, String clientSecret) {
RestTemplate template = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("grant_type", "authorization_code");
map.add("code", code);
map.add("redirect_uri", redirectUri);
map.add("client_id", clientId);
map.add("client_secret", clientSecret);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
Map<String, Object> values = template.postForObject(this.environment.getProperty("linkedin.login.access_token_url"), request, Map.class);
LinkedInResponseToken linkedInResponseToken = new LinkedInResponseToken();
linkedInResponseToken.setAccessToken((String) values.get("access_token"));
linkedInResponseToken.setExpiresIn((Integer) values.get("expires_in"));
return linkedInResponseToken;
}
private HttpHeaders createBearerAuthHeaders(String accessToken) {
return new HttpHeaders() {{
String authHeader = "Bearer " + new String(accessToken);
set("Authorization", authHeader);
}};
}
}

View File

@ -0,0 +1,29 @@
package eu.eudat.logic.security.customproviders.LinkedIn;
public class LinkedInUser {
private String id;
private String name;
private String email;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

View File

@ -1,6 +1,8 @@
package eu.eudat.logic.security.validators;
import eu.eudat.logic.security.customproviders.B2Access.B2AccessCustomProvider;
import eu.eudat.logic.security.customproviders.LinkedIn.LinkedInCustomProvider;
import eu.eudat.logic.security.customproviders.LinkedIn.LinkedInCustomProviderImpl;
import eu.eudat.logic.security.customproviders.ORCID.ORCIDCustomProvider;
import eu.eudat.logic.security.validators.b2access.B2AccessTokenValidator;
import eu.eudat.logic.security.validators.facebook.FacebookTokenValidator;
@ -57,14 +59,19 @@ public class TokenValidatorFactoryImpl implements TokenValidatorFactory {
private AuthenticationService nonVerifiedUserAuthenticationService;
private B2AccessCustomProvider b2AccessCustomProvider;
private ORCIDCustomProvider orcidCustomProvider;
private LinkedInCustomProvider linkedInCustomProvider;
@Autowired
public TokenValidatorFactoryImpl(ApiContext apiContext, Environment environment, AuthenticationService nonVerifiedUserAuthenticationService, B2AccessCustomProvider b2AccessCustomProvider, ORCIDCustomProvider orcidCustomProvider) {
public TokenValidatorFactoryImpl(
ApiContext apiContext, Environment environment,
AuthenticationService nonVerifiedUserAuthenticationService, B2AccessCustomProvider b2AccessCustomProvider,
ORCIDCustomProvider orcidCustomProvider, LinkedInCustomProvider linkedInCustomProvider) {
this.apiContext = apiContext;
this.environment = environment;
this.nonVerifiedUserAuthenticationService = nonVerifiedUserAuthenticationService;
this.b2AccessCustomProvider = b2AccessCustomProvider;
this.orcidCustomProvider = orcidCustomProvider;
this.linkedInCustomProvider = linkedInCustomProvider;
}
public TokenValidator getProvider(LoginProvider provider) {
@ -74,13 +81,13 @@ public class TokenValidatorFactoryImpl implements TokenValidatorFactory {
case FACEBOOK:
return new FacebookTokenValidator(this.apiContext, this.environment, this.nonVerifiedUserAuthenticationService);
case LINKEDIN:
return new LinkedInTokenValidator(this.apiContext, this.environment, this.nonVerifiedUserAuthenticationService);
return new LinkedInTokenValidator(this.environment, this.nonVerifiedUserAuthenticationService, linkedInCustomProvider);
case TWITTER:
return new TwitterTokenValidator(this.apiContext, this.environment, this.nonVerifiedUserAuthenticationService);
case B2_ACCESS:
return new B2AccessTokenValidator(this.environment, this.nonVerifiedUserAuthenticationService, this.b2AccessCustomProvider);
case ORCID:
return new ORCIDTokenValidator(this.environment, this.nonVerifiedUserAuthenticationService, this.orcidCustomProvider, this.apiContext);
return new ORCIDTokenValidator(this.environment, this.nonVerifiedUserAuthenticationService, this.orcidCustomProvider);
default:
throw new RuntimeException("Login Provider Not Implemented");
}

View File

@ -1,8 +1,12 @@
package eu.eudat.logic.security.validators.linkedin;
import eu.eudat.exceptions.security.UnauthorisedException;
import eu.eudat.logic.security.customproviders.LinkedIn.LinkedInCustomProvider;
import eu.eudat.logic.security.customproviders.LinkedIn.LinkedInUser;
import eu.eudat.logic.security.validators.TokenValidator;
import eu.eudat.logic.security.validators.TokenValidatorFactoryImpl;
import eu.eudat.logic.security.validators.linkedin.helpers.LinkedInRequest;
import eu.eudat.logic.security.validators.linkedin.helpers.LinkedInResponseToken;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.logic.services.operations.authentication.AuthenticationService;
import eu.eudat.models.data.login.LoginInfo;
@ -21,21 +25,21 @@ import org.springframework.stereotype.Component;
public class LinkedInTokenValidator implements TokenValidator {
private Environment environment;
private ApiContext apiContext;
private AuthenticationService nonVerifiedUserAuthenticationService;
private LinkedInServiceProvider linkedInServiceProvider;
private LinkedInCustomProvider linkedInCustomProvider;
@Autowired
public LinkedInTokenValidator(ApiContext apiContext, Environment environment, AuthenticationService nonVerifiedUserAuthenticationService) {
public LinkedInTokenValidator(Environment environment, AuthenticationService nonVerifiedUserAuthenticationService, LinkedInCustomProvider linkedInCustomProvider) {
this.environment = environment;
this.apiContext = apiContext;
this.nonVerifiedUserAuthenticationService = nonVerifiedUserAuthenticationService;
this.linkedInServiceProvider = new LinkedInServiceProvider(this.environment.getProperty("linkedin.login.clientId"), this.environment.getProperty("linkedin.login.clientSecret"));
this.linkedInCustomProvider = linkedInCustomProvider;
}
@Override
public Principal validateToken(LoginInfo credentials) {
AccessGrant accessGrant = this.linkedInServiceProvider.getOAuthOperations().exchangeForAccess(credentials.getTicket(), this.environment.getProperty("linkedin.login.redirect_uri"), null);
/*AccessGrant accessGrant = this.linkedInServiceProvider.getOAuthOperations().exchangeForAccess(credentials.getTicket(), this.environment.getProperty("linkedin.login.redirect_uri"), null);
LinkedIn linkedInService = this.linkedInServiceProvider.getApi(accessGrant.getAccessToken());
LinkedInProfile linkedInProfile = linkedInService.profileOperations().getUserProfile();
LoginProviderUser user = new LoginProviderUser();
@ -48,7 +52,24 @@ public class LinkedInTokenValidator implements TokenValidator {
user.setAvatarUrl(linkedInProfile.getProfilePictureUrl());
user.setName(linkedInProfile.getFirstName() + " " + linkedInProfile.getLastName());
user.setProvider(TokenValidatorFactoryImpl.LoginProvider.LINKEDIN);
user.setSecret(accessGrant.getAccessToken());
user.setSecret(accessGrant.getAccessToken());*/
LinkedInUser linkedInUser = this.linkedInCustomProvider.getUser(credentials.getTicket());
if (linkedInUser.getEmail() == null)
throw new UnauthorisedException("Cannot login user.LinkedIn account did not provide email");
LoginProviderUser user = new LoginProviderUser();
user.setId(linkedInUser.getId());
user.setName(linkedInUser.getName());
user.setEmail(linkedInUser.getEmail());
user.setProvider(credentials.getProvider());
user.setSecret(credentials.getTicket());
return this.nonVerifiedUserAuthenticationService.Touch(user);
}
public LinkedInResponseToken getAccessToken(LinkedInRequest linkedInRequest) {
return this.linkedInCustomProvider.getAccessToken(
linkedInRequest.getCode(), this.environment.getProperty("linkedin.login.redirect_uri"),
this.environment.getProperty("linkedin.login.clientId"), this.environment.getProperty("linkedin.login.clientSecret"));
}
}

View File

@ -0,0 +1,12 @@
package eu.eudat.logic.security.validators.linkedin.helpers;
public class LinkedInRequest {
private String code;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}

View File

@ -0,0 +1,21 @@
package eu.eudat.logic.security.validators.linkedin.helpers;
public class LinkedInResponseToken {
private String accessToken;
private Integer expiresIn;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public Integer getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(Integer expiresIn) {
this.expiresIn = expiresIn;
}
}

View File

@ -7,7 +7,6 @@ import eu.eudat.logic.security.customproviders.ORCID.ORCIDUser;
import eu.eudat.logic.security.validators.TokenValidator;
import eu.eudat.logic.security.validators.orcid.helpers.ORCIDRequest;
import eu.eudat.logic.security.validators.orcid.helpers.ORCIDResponseToken;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.logic.services.operations.authentication.AuthenticationService;
import eu.eudat.models.data.login.LoginInfo;
import eu.eudat.models.data.loginprovider.LoginProviderUser;
@ -25,14 +24,12 @@ public class ORCIDTokenValidator implements TokenValidator {
private ORCIDCustomProvider orcidCustomProvider;
private Environment environment;
private AuthenticationService nonVerifiedUserAuthenticationService;
private ApiContext apiContext;
@Autowired
public ORCIDTokenValidator(Environment environment, AuthenticationService nonVerifiedUserAuthenticationService, ORCIDCustomProvider orcidCustomProvider, ApiContext apiContext) {
public ORCIDTokenValidator(Environment environment, AuthenticationService nonVerifiedUserAuthenticationService, ORCIDCustomProvider orcidCustomProvider) {
this.environment = environment;
this.nonVerifiedUserAuthenticationService = nonVerifiedUserAuthenticationService;
this.orcidCustomProvider = orcidCustomProvider;
this.apiContext = apiContext;
}
@Override

View File

@ -29,7 +29,12 @@ dataset.tags.mock=/mockupTags.json
twitter.login.redirect_uri=http://127.0.0.1:4200/login/twitter
#############LINKEDIN LOGIN CONFIGURATIONS#########
linkedin.login.clientId=
linkedin.login.clientSecret=
linkedin.login.redirect_uri=http://localhost:4200/login/linkedin
linkedin.login.user_info_url=https://api.linkedin.com/v2/me
linkedin.login.user_email=https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))
linkedin.login.access_token_url=https://www.linkedin.com/uas/oauth2/accessToken
#############FACEBOOK LOGIN CONFIGURATIONS#########
facebook.login.clientId=

View File

@ -46,6 +46,9 @@ google.login.clientId=
linkedin.login.clientId=
linkedin.login.clientSecret=
linkedin.login.redirect_uri=https://opendmp.eu/login/linkedin
linkedin.login.user_info_url=https://api.linkedin.com/v2/me
linkedin.login.user_email=https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))
linkedin.login.access_token_url=https://www.linkedin.com/uas/oauth2/accessToken
#############TWITTER LOGIN CONFIGURATIONS#########
twitter.login.clientId=

View File

@ -48,6 +48,9 @@ google.login.clientId=
linkedin.login.clientId=
linkedin.login.clientSecret=
linkedin.login.redirect_uri=https://devel.opendmp.eu/login/linkedin
linkedin.login.user_info_url=https://api.linkedin.com/v2/me
linkedin.login.user_email=https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))
linkedin.login.access_token_url=https://www.linkedin.com/uas/oauth2/accessToken
#############TWITTER LOGIN CONFIGURATIONS#########
twitter.login.clientId=

View File

@ -37,6 +37,9 @@ google.login.clientId=
linkedin.login.clientId=
linkedin.login.clientSecret=
linkedin.login.redirect_uri=http://opendmp.eu/login/linkedin
linkedin.login.user_info_url=https://api.linkedin.com/v2/me
linkedin.login.user_email=https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))
linkedin.login.access_token_url=https://www.linkedin.com/uas/oauth2/accessToken
#############TWITTER LOGIN CONFIGURATIONS#########
twitter.login.clientId=

View File

@ -1,11 +1,12 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { takeUntil } from 'rxjs/operators';
import { environment } from '../../../../../environments/environment';
import { BaseComponent } from '../../../../core/common/base/base.component';
import { AuthProvider } from '../../../../core/common/enum/auth-provider';
import { AuthService } from '../../../../core/services/auth/auth.service';
import { LoginService } from '../utilities/login.service';
import { HttpClient } from "@angular/common/http";
@Component({
selector: 'app-linkedin-login',
@ -18,7 +19,9 @@ export class LinkedInLoginComponent extends BaseComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private loginService: LoginService,
private authService: AuthService
private authService: AuthService,
private router: Router,
private httpClient: HttpClient
) {
super();
}
@ -29,21 +32,31 @@ export class LinkedInLoginComponent extends BaseComponent implements OnInit {
.subscribe((params: Params) => {
const returnUrl = params['returnUrl'];
if (returnUrl) { this.returnUrl = returnUrl; }
if (!params['code']) { this.linkedinAuthorize(); } else { this.linkedInLoginUser(params['code']); }
if (!params['code']) { this.linkedinAuthorize(); } else { this.linkedInLoginUser(params['code'], params['state']); }
});
}
public linkedinAuthorize() {
window.location.href = environment.loginProviders.linkedInConfiguration.oauthUrl + '?response_type=code&client_id=' + environment.loginProviders.linkedInConfiguration.clientId + '&redirect_uri=' + environment.loginProviders.linkedInConfiguration.redirectUri + '&state=987654321';
window.location.href = environment.loginProviders.linkedInConfiguration.oauthUrl
+ '?response_type=code&client_id=' + environment.loginProviders.linkedInConfiguration.clientId
+ '&redirect_uri=' + environment.loginProviders.linkedInConfiguration.redirectUri
+ '&state=' + environment.loginProviders.linkedInConfiguration.state
+ '&scope=r_emailaddress';
}
public linkedInLoginUser(code: string) {
this.authService.login({ ticket: code, provider: AuthProvider.LinkedIn })
public linkedInLoginUser(code: string, state: string) {
if (state !== environment.loginProviders.linkedInConfiguration.state) {
this.router.navigate(['/login']);
}
this.httpClient.post(environment.Server + 'auth/linkedInRequestToken', { code: code, provider: AuthProvider.LinkedIn })
.pipe(takeUntil(this._destroyed))
.subscribe(
res => this.loginService.onLogInSuccess(res, this.returnUrl),
error => this.loginService.onLogInError(error)
);
.subscribe((data: any) => {
this.authService.login({ ticket: data.payload.accessToken, provider: AuthProvider.LinkedIn, data: null })
.pipe(takeUntil(this._destroyed))
.subscribe(
res => this.loginService.onLogInSuccess(res, this.returnUrl),
error => this.loginService.onLogInError(error)
);
});
}
}

View File

@ -44,11 +44,11 @@ export class LoginService extends BaseService {
public onLogInSuccess(loginResponse: any, returnUrl: string) {
this.zone.run(() => {
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-LOGIN'), SnackBarNotificationLevel.Success);
if (this.authService.current().culture) { this.cultureService.cultureSelected(this.authService.current().culture); }
const redirectUrl = returnUrl || '/';
this.router.navigate([redirectUrl]);
});
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-LOGIN'), SnackBarNotificationLevel.Success);
if (this.authService.current().culture) { this.cultureService.cultureSelected(this.authService.current().culture); }
const redirectUrl = returnUrl || '/';
this.router.navigate([redirectUrl]);
});
}
public onLogInError(errorMessage: string) {