diff --git a/dmp-backend/src/main/java/eu/eudat/builders/entity/CredentialBuilder.java b/dmp-backend/src/main/java/eu/eudat/builders/entity/CredentialBuilder.java index 28f83d454..b15c8d56e 100644 --- a/dmp-backend/src/main/java/eu/eudat/builders/entity/CredentialBuilder.java +++ b/dmp-backend/src/main/java/eu/eudat/builders/entity/CredentialBuilder.java @@ -28,6 +28,8 @@ public class CredentialBuilder extends Builder { private Date lastUpdateTime; + private String externalId; + public CredentialBuilder id(UUID id) { this.id = id; return this; @@ -68,6 +70,11 @@ public class CredentialBuilder extends Builder { return this; } + public CredentialBuilder externalId(String externalId) { + this.externalId = externalId; + return this; + } + public Credential build() { Credential credential = new Credential(); credential.setStatus(status); @@ -78,6 +85,7 @@ public class CredentialBuilder extends Builder { credential.setPublicValue(publicValue); credential.setUserInfo(userInfo); credential.setId(id); + credential.setExternalId(externalId); return credential; } } diff --git a/dmp-backend/src/main/java/eu/eudat/controllers/Login.java b/dmp-backend/src/main/java/eu/eudat/controllers/Login.java index fa8596799..e40c65a11 100644 --- a/dmp-backend/src/main/java/eu/eudat/controllers/Login.java +++ b/dmp-backend/src/main/java/eu/eudat/controllers/Login.java @@ -7,6 +7,9 @@ import eu.eudat.models.login.Credentials; import eu.eudat.models.login.LoginInfo; import eu.eudat.models.security.Principal; import eu.eudat.security.CustomAuthenticationProvider; +import eu.eudat.security.validators.b2access.B2AccessTokenValidator; +import eu.eudat.security.validators.b2access.helpers.B2AccessRequest; +import eu.eudat.security.validators.b2access.helpers.B2AccessResponseToken; import eu.eudat.security.validators.twitter.TwitterTokenValidator; import eu.eudat.services.AuthenticationService; import eu.eudat.types.ApiMessageCode; @@ -30,11 +33,14 @@ public class Login { private TwitterTokenValidator twitterTokenValidator; + private B2AccessTokenValidator b2AccessTokenValidator; + @Autowired - public Login(CustomAuthenticationProvider customAuthenticationProvider, AuthenticationService authenticationService, TwitterTokenValidator twitterTokenValidator) { + public Login(CustomAuthenticationProvider customAuthenticationProvider, AuthenticationService authenticationService, TwitterTokenValidator twitterTokenValidator, B2AccessTokenValidator b2AccessTokenValidator) { this.customAuthenticationProvider = customAuthenticationProvider; this.authenticationService = authenticationService; this.twitterTokenValidator = twitterTokenValidator; + this.b2AccessTokenValidator = b2AccessTokenValidator; } @Transactional @@ -76,6 +82,17 @@ public class Login { } } + @RequestMapping(method = RequestMethod.POST, value = {"/b2AccessRequestToken"}, produces = "application/json", consumes = "application/json") + public @ResponseBody + ResponseEntity> b2AccessRequestToken(@RequestBody B2AccessRequest b2AccessRequest) { + try { + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(this.b2AccessTokenValidator.getAccessToken(b2AccessRequest)).status(ApiMessageCode.NO_MESSAGE)); + } catch (Exception ex) { + ex.printStackTrace(); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem().status(ApiMessageCode.DEFAULT_ERROR_MESSAGE).message(ex.getMessage())); + } + } + @RequestMapping(method = RequestMethod.POST, value = {"/me"}, consumes = "application/json", produces = "application/json") public @ResponseBody ResponseEntity> authMe(Principal principal) { diff --git a/dmp-backend/src/main/java/eu/eudat/entities/Credential.java b/dmp-backend/src/main/java/eu/eudat/entities/Credential.java index 254ce8188..070d39cb0 100644 --- a/dmp-backend/src/main/java/eu/eudat/entities/Credential.java +++ b/dmp-backend/src/main/java/eu/eudat/entities/Credential.java @@ -7,6 +7,11 @@ import java.util.UUID; @Entity @Table(name = "\"Credential\"") +@NamedEntityGraphs({ + @NamedEntityGraph( + name = "credentialUserInfo", + attributeNodes = {@NamedAttributeNode("userInfo")}) +}) public class Credential implements DataEntity { @Id @@ -31,6 +36,9 @@ public class Credential implements DataEntity { @Column(name = "\"LastUpdateTime\"", nullable = false) private Date lastUpdateTime; + @Column(name = "\"ExternalId\"", nullable = false) + private String externalId; + public UUID getId() { return id; } @@ -95,6 +103,14 @@ public class Credential implements DataEntity { this.lastUpdateTime = lastUpdateTime; } + public String getExternalId() { + return externalId; + } + + public void setExternalId(String externalId) { + this.externalId = externalId; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/dmp-backend/src/main/java/eu/eudat/managers/PaginationManager.java b/dmp-backend/src/main/java/eu/eudat/managers/PaginationManager.java index 6473fc8b9..d7bce76ac 100644 --- a/dmp-backend/src/main/java/eu/eudat/managers/PaginationManager.java +++ b/dmp-backend/src/main/java/eu/eudat/managers/PaginationManager.java @@ -6,6 +6,7 @@ import eu.eudat.models.helpers.common.Ordering; import eu.eudat.models.helpers.requests.TableRequest; import eu.eudat.queryable.QueryableList; +import java.util.Arrays; import java.util.Collection; public class PaginationManager { @@ -14,6 +15,8 @@ public class PaginationManager { if (tableRequest.getOrderings() != null) applyOrder(items, tableRequest); if (tableRequest.getLength() != null) items.take(tableRequest.getLength()); if (tableRequest.getOffset() != null) items.skip(tableRequest.getOffset()); + if (tableRequest.getSelection() != null && tableRequest.getSelection().getFields() != null && tableRequest.getSelection().getFields().length > 0) + items.withFields(Arrays.asList(tableRequest.getSelection().getFields())); return items; } diff --git a/dmp-backend/src/main/java/eu/eudat/models/loginprovider/LoginProviderUser.java b/dmp-backend/src/main/java/eu/eudat/models/loginprovider/LoginProviderUser.java index ded6cebf7..61434530a 100644 --- a/dmp-backend/src/main/java/eu/eudat/models/loginprovider/LoginProviderUser.java +++ b/dmp-backend/src/main/java/eu/eudat/models/loginprovider/LoginProviderUser.java @@ -5,11 +5,20 @@ import eu.eudat.security.validators.TokenValidatorFactoryImpl; public class LoginProviderUser { private String name; + private String id; private String email; private String secret; private boolean isVerified; private TokenValidatorFactoryImpl.LoginProvider provider; + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + public String getName() { return name; } diff --git a/dmp-backend/src/main/java/eu/eudat/queryable/QueryableList.java b/dmp-backend/src/main/java/eu/eudat/queryable/QueryableList.java index 60e633d2e..416e1d0ba 100644 --- a/dmp-backend/src/main/java/eu/eudat/queryable/QueryableList.java +++ b/dmp-backend/src/main/java/eu/eudat/queryable/QueryableList.java @@ -22,6 +22,8 @@ public interface QueryableList { List toList(); + QueryableList withFields(List fields); + CompletableFuture> toListAsync(); T getSingle(); diff --git a/dmp-backend/src/main/java/eu/eudat/queryable/hibernatequeryablelist/QueryableHibernateList.java b/dmp-backend/src/main/java/eu/eudat/queryable/hibernatequeryablelist/QueryableHibernateList.java index 34608de0b..d0b56eb64 100644 --- a/dmp-backend/src/main/java/eu/eudat/queryable/hibernatequeryablelist/QueryableHibernateList.java +++ b/dmp-backend/src/main/java/eu/eudat/queryable/hibernatequeryablelist/QueryableHibernateList.java @@ -33,7 +33,7 @@ public class QueryableHibernateList implements QueryableLi private List> nestedPredicates = new LinkedList<>(); private List> orderings = new LinkedList<>(); - private List fields = new LinkedList<>(); + private List fields = new LinkedList<>(); private Integer length; private Integer offset; private Set hints; @@ -59,6 +59,18 @@ public class QueryableHibernateList implements QueryableLi return this; } + @Override + public QueryableList withFields(List fields) { + this.fields = fields; + return this; + } + + private QueryableList selectFields() { + List rootFields = fields.stream().map(field -> root.get(field)).collect(Collectors.toList()); + this.query.select(this.manager.getCriteriaBuilder().construct(tClass, rootFields.toArray(new Selection[rootFields.size()]))); + return this; + } + public QueryableHibernateList setEntity(Class type) { CriteriaBuilder builder = this.manager.getCriteriaBuilder(); this.query = builder.createQuery(type); @@ -98,7 +110,7 @@ public class QueryableHibernateList implements QueryableLi } public CompletableFuture> selectAsync(SelectPredicate predicate) { - return this.toListAsync().thenApplyAsync(items-> items.stream().map(item -> predicate.applySelection(item)).collect(Collectors.toList())); + return this.toListAsync().thenApplyAsync(items -> items.stream().map(item -> predicate.applySelection(item)).collect(Collectors.toList())); } public QueryableList distinct() { @@ -162,9 +174,9 @@ public class QueryableHibernateList implements QueryableLi } public List toList() { - this.query.where(this.generateWherePredicates(this.singlePredicates, this.root, this.nestedPredicates, this.nestedQueryRoot)); if (!this.orderings.isEmpty()) this.query.orderBy(this.generateOrderPredicates(this.orderings, this.root)); + if (this.fields != null && !this.fields.isEmpty()) this.selectFields(); TypedQuery typedQuery = this.manager.createQuery(this.query); if (this.offset != null) typedQuery.setFirstResult(this.offset); if (this.length != null) typedQuery.setMaxResults(this.length); @@ -178,6 +190,7 @@ public class QueryableHibernateList implements QueryableLi public CompletableFuture> toListAsync() { this.query.where(this.generateWherePredicates(this.singlePredicates, this.root, this.nestedPredicates, this.nestedQueryRoot)); if (!this.orderings.isEmpty()) this.query.orderBy(this.generateOrderPredicates(this.orderings, this.root)); + if (this.fields != null && !this.fields.isEmpty()) this.selectFields(); TypedQuery typedQuery = this.manager.createQuery(this.query); if (this.offset != null) typedQuery.setFirstResult(this.offset); if (this.length != null) typedQuery.setMaxResults(this.length); @@ -192,6 +205,7 @@ public class QueryableHibernateList implements QueryableLi public T getSingle() { this.query.where(this.generateWherePredicates(this.singlePredicates, this.root, this.nestedPredicates, this.nestedQueryRoot)); + if (this.fields != null && !this.fields.isEmpty()) this.selectFields(); TypedQuery typedQuery = this.manager.createQuery(this.query); if (this.hint != null) typedQuery.setHint("javax.persistence.fetchgraph", this.manager.getEntityGraph(this.hint)); @@ -200,6 +214,7 @@ public class QueryableHibernateList implements QueryableLi public CompletableFuture getSingleAsync() { this.query.where(this.generateWherePredicates(this.singlePredicates, this.root, this.nestedPredicates, this.nestedQueryRoot)); + if (this.fields != null && !this.fields.isEmpty()) this.selectFields(); TypedQuery typedQuery = this.manager.createQuery(this.query); if (this.hint != null) typedQuery.setHint("javax.persistence.fetchgraph", this.manager.getEntityGraph(this.hint)); @@ -208,7 +223,7 @@ public class QueryableHibernateList implements QueryableLi public T getSingleOrDefault() { this.query.where(this.generateWherePredicates(this.singlePredicates, this.root, this.nestedPredicates, this.nestedQueryRoot)); - + if (this.fields != null && !this.fields.isEmpty()) this.selectFields(); TypedQuery typedQuery = this.manager.createQuery(this.query); if (this.hint != null) typedQuery.setHint("javax.persistence.fetchgraph", this.manager.getEntityGraph(this.hint)); @@ -220,7 +235,7 @@ public class QueryableHibernateList implements QueryableLi public CompletableFuture getSingleOrDefaultAsync() { this.query.where(this.generateWherePredicates(this.singlePredicates, this.root, this.nestedPredicates, this.nestedQueryRoot)); - + if (this.fields != null && !this.fields.isEmpty()) this.selectFields(); TypedQuery typedQuery = this.manager.createQuery(this.query); if (this.hint != null) typedQuery.setHint("javax.persistence.fetchgraph", this.manager.getEntityGraph(this.hint)); diff --git a/dmp-backend/src/main/java/eu/eudat/security/customproviders/B2AccessCustomProvider.java b/dmp-backend/src/main/java/eu/eudat/security/customproviders/B2AccessCustomProvider.java new file mode 100644 index 000000000..2d35b1336 --- /dev/null +++ b/dmp-backend/src/main/java/eu/eudat/security/customproviders/B2AccessCustomProvider.java @@ -0,0 +1,12 @@ +package eu.eudat.security.customproviders; + +import eu.eudat.security.validators.b2access.helpers.B2AccessResponseToken; + +/** + * Created by ikalyvas on 2/22/2018. + */ +public interface B2AccessCustomProvider { + B2AccessUser getUser(String accessToken); + + B2AccessResponseToken getAccessToken(String code, String redirectUri, String clientId, String clientSecret); +} diff --git a/dmp-backend/src/main/java/eu/eudat/security/customproviders/B2AccessCustomProviderImpl.java b/dmp-backend/src/main/java/eu/eudat/security/customproviders/B2AccessCustomProviderImpl.java new file mode 100644 index 000000000..4c3880d13 --- /dev/null +++ b/dmp-backend/src/main/java/eu/eudat/security/customproviders/B2AccessCustomProviderImpl.java @@ -0,0 +1,80 @@ +package eu.eudat.security.customproviders; + +import com.google.api.client.repackaged.org.apache.commons.codec.binary.Base64; +import eu.eudat.security.validators.b2access.helpers.B2AccessResponseToken; +import org.springframework.beans.factory.annotation.Autowired; +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.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.nio.charset.Charset; +import java.util.Map; + +/** + * Created by ikalyvas on 2/22/2018. + */ +@Component("b2AccessCustomProvider") +public class B2AccessCustomProviderImpl implements B2AccessCustomProvider { + + private Environment environment; + + @Autowired + public B2AccessCustomProviderImpl(Environment environment) { + this.environment = environment; + } + + public B2AccessUser getUser(String accessToken) { + RestTemplate restTemplate = new RestTemplate(); + HttpHeaders headers = this.createBearerAuthHeaders(accessToken); + HttpEntity entity = new HttpEntity<>(headers); + + Map values = restTemplate.exchange(this.environment.getProperty("b2access.externallogin.user_info_url"), HttpMethod.GET, entity, Map.class).getBody(); + B2AccessUser b2AccessUser = new B2AccessUser(); + b2AccessUser.setEmail((String)values.get("email")); + b2AccessUser.setId((String)values.get("urn:oid:2.5.4.49")); + b2AccessUser.setName((String)values.get("name")); + return b2AccessUser; + } + + @Override + public B2AccessResponseToken getAccessToken(String code, String redirectUri, String clientId, String clientSecret) { + RestTemplate template = new RestTemplate(); + HttpHeaders headers = this.createBasicAuthHeaders(clientId, clientSecret); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap map = new LinkedMultiValueMap(); + map.add("code", code); + map.add("grant_type", "authorization_code"); + map.add("redirect_uri", redirectUri); + HttpEntity> request = new HttpEntity>(map, headers); + + + Map values = template.postForObject(this.environment.getProperty("b2access.externallogin.access_token_url"), request, Map.class); + B2AccessResponseToken b2AccessResponseToken = new B2AccessResponseToken(); + b2AccessResponseToken.setAccessToken((String) values.get("access_token")); + return b2AccessResponseToken; + } + + private HttpHeaders createBasicAuthHeaders(String username, String password) { + return new HttpHeaders() {{ + String auth = username + ":" + password; + byte[] encodedAuth = Base64.encodeBase64( + auth.getBytes(Charset.forName("US-ASCII"))); + String authHeader = "Basic " + new String(encodedAuth); + set("Authorization", authHeader); + }}; + } + + private HttpHeaders createBearerAuthHeaders(String accessToken) { + return new HttpHeaders() {{ + String authHeader = "Bearer " + new String(accessToken); + set("Authorization", authHeader); + }}; + } +} diff --git a/dmp-backend/src/main/java/eu/eudat/security/customproviders/B2AccessUser.java b/dmp-backend/src/main/java/eu/eudat/security/customproviders/B2AccessUser.java new file mode 100644 index 000000000..4b8d23ece --- /dev/null +++ b/dmp-backend/src/main/java/eu/eudat/security/customproviders/B2AccessUser.java @@ -0,0 +1,34 @@ +package eu.eudat.security.customproviders; + +/** + * Created by ikalyvas on 2/22/2018. + */ +public class B2AccessUser { + 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; + } +} diff --git a/dmp-backend/src/main/java/eu/eudat/security/validators/TokenValidatorFactoryImpl.java b/dmp-backend/src/main/java/eu/eudat/security/validators/TokenValidatorFactoryImpl.java index e147957ff..0904f5e82 100644 --- a/dmp-backend/src/main/java/eu/eudat/security/validators/TokenValidatorFactoryImpl.java +++ b/dmp-backend/src/main/java/eu/eudat/security/validators/TokenValidatorFactoryImpl.java @@ -1,5 +1,6 @@ package eu.eudat.security.validators; +import eu.eudat.security.validators.b2access.B2AccessTokenValidator; import eu.eudat.security.validators.facebook.FacebookTokenValidator; import eu.eudat.security.validators.google.GoogleTokenValidator; import eu.eudat.security.validators.linkedin.LinkedInTokenValidator; @@ -11,7 +12,7 @@ import org.springframework.stereotype.Service; @Service("tokenValidatorFactory") public class TokenValidatorFactoryImpl implements TokenValidatorFactory { public enum LoginProvider { - GOOGLE((short) 1), FACEBOOK((short) 2), TWITTER((short) 3), LINKEDIN((short) 4), NATIVELOGIN((short) 5); + GOOGLE((short) 1), FACEBOOK((short) 2), TWITTER((short) 3), LINKEDIN((short) 4), NATIVELOGIN((short) 5), B2_ACCESS((short) 6); private short value; @@ -35,6 +36,8 @@ public class TokenValidatorFactoryImpl implements TokenValidatorFactory { return LINKEDIN; case 5: return NATIVELOGIN; + case 6: + return B2_ACCESS; default: throw new RuntimeException("Unsupported LoginProvider"); } @@ -45,13 +48,16 @@ public class TokenValidatorFactoryImpl implements TokenValidatorFactory { private FacebookTokenValidator facebookTokenValidator; private LinkedInTokenValidator linkedInTokenValidator; private TwitterTokenValidator twitterTokenValidator; + private B2AccessTokenValidator b2AccessTokenValidator; @Autowired - public TokenValidatorFactoryImpl(GoogleTokenValidator googleTokenValidator, FacebookTokenValidator facebookTokenValidator, LinkedInTokenValidator linkedInTokenValidator, TwitterTokenValidator twitterTokenValidator) { + public TokenValidatorFactoryImpl(GoogleTokenValidator googleTokenValidator, FacebookTokenValidator facebookTokenValidator, + LinkedInTokenValidator linkedInTokenValidator, TwitterTokenValidator twitterTokenValidator,B2AccessTokenValidator b2AccessTokenValidator) { this.googleTokenValidator = googleTokenValidator; this.facebookTokenValidator = facebookTokenValidator; this.linkedInTokenValidator = linkedInTokenValidator; this.twitterTokenValidator = twitterTokenValidator; + this.b2AccessTokenValidator = b2AccessTokenValidator; } public TokenValidator getProvider(LoginProvider provider) { @@ -64,6 +70,8 @@ public class TokenValidatorFactoryImpl implements TokenValidatorFactory { return this.linkedInTokenValidator; case TWITTER: return this.twitterTokenValidator; + case B2_ACCESS: + return this.b2AccessTokenValidator; default: throw new RuntimeException("Login Provider Not Implemented"); } diff --git a/dmp-backend/src/main/java/eu/eudat/security/validators/b2access/B2AccessTokenValidator.java b/dmp-backend/src/main/java/eu/eudat/security/validators/b2access/B2AccessTokenValidator.java new file mode 100644 index 000000000..04660739b --- /dev/null +++ b/dmp-backend/src/main/java/eu/eudat/security/validators/b2access/B2AccessTokenValidator.java @@ -0,0 +1,54 @@ +package eu.eudat.security.validators.b2access; + +import eu.eudat.exceptions.security.NonValidTokenException; +import eu.eudat.models.login.LoginInfo; +import eu.eudat.models.loginprovider.LoginProviderUser; +import eu.eudat.models.security.Principal; +import eu.eudat.security.customproviders.B2AccessCustomProvider; +import eu.eudat.security.customproviders.B2AccessUser; +import eu.eudat.security.validators.TokenValidator; +import eu.eudat.security.validators.b2access.helpers.B2AccessRequest; +import eu.eudat.security.validators.b2access.helpers.B2AccessResponseToken; +import eu.eudat.services.AuthenticationService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +/** + * Created by ikalyvas on 2/22/2018. + */ +@Component("b2AccessTokenValidator ") +public class B2AccessTokenValidator implements TokenValidator { + + private B2AccessCustomProvider b2AccessCustomProvider; + private AuthenticationService authenticationService; + private Environment environment; + + @Autowired + public B2AccessTokenValidator(AuthenticationService authenticationService, Environment environment, B2AccessCustomProvider b2AccessCustomProvider) { + this.authenticationService = authenticationService; + this.environment = environment; + this.b2AccessCustomProvider = b2AccessCustomProvider; + } + + @Override + public Principal validateToken(LoginInfo credentials) throws NonValidTokenException, IOException, GeneralSecurityException { + B2AccessUser b2AccessUser = this.b2AccessCustomProvider.getUser(credentials.getTicket()); + LoginProviderUser user = new LoginProviderUser(); + user.setId(b2AccessUser.getId()); + user.setEmail(b2AccessUser.getEmail()); + user.setName(b2AccessUser.getName()); + user.setProvider(credentials.getProvider()); + user.setSecret(credentials.getTicket()); + return this.authenticationService.Touch(user); + } + + public B2AccessResponseToken getAccessToken(B2AccessRequest b2AccessRequest) { + return this.b2AccessCustomProvider.getAccessToken(b2AccessRequest.getCode(), this.environment.getProperty("b2access.externallogin.redirect_uri") + , this.environment.getProperty("b2access.externallogin.clientid") + , this.environment.getProperty("b2access.externallogin.clientSecret")); + } +} diff --git a/dmp-backend/src/main/java/eu/eudat/security/validators/b2access/helpers/B2AccessRequest.java b/dmp-backend/src/main/java/eu/eudat/security/validators/b2access/helpers/B2AccessRequest.java new file mode 100644 index 000000000..85a5947d3 --- /dev/null +++ b/dmp-backend/src/main/java/eu/eudat/security/validators/b2access/helpers/B2AccessRequest.java @@ -0,0 +1,16 @@ +package eu.eudat.security.validators.b2access.helpers; + +/** + * Created by ikalyvas on 2/22/2018. + */ +public class B2AccessRequest { + private String code; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } +} diff --git a/dmp-backend/src/main/java/eu/eudat/security/validators/b2access/helpers/B2AccessResponseToken.java b/dmp-backend/src/main/java/eu/eudat/security/validators/b2access/helpers/B2AccessResponseToken.java new file mode 100644 index 000000000..16d042f15 --- /dev/null +++ b/dmp-backend/src/main/java/eu/eudat/security/validators/b2access/helpers/B2AccessResponseToken.java @@ -0,0 +1,16 @@ +package eu.eudat.security.validators.b2access.helpers; + +/** + * Created by ikalyvas on 2/22/2018. + */ +public class B2AccessResponseToken { + private String accessToken; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } +} diff --git a/dmp-backend/src/main/java/eu/eudat/security/validators/facebook/FacebookTokenValidator.java b/dmp-backend/src/main/java/eu/eudat/security/validators/facebook/FacebookTokenValidator.java index a72e5be01..50679406f 100644 --- a/dmp-backend/src/main/java/eu/eudat/security/validators/facebook/FacebookTokenValidator.java +++ b/dmp-backend/src/main/java/eu/eudat/security/validators/facebook/FacebookTokenValidator.java @@ -44,6 +44,7 @@ public class FacebookTokenValidator implements TokenValidator { if (profile.getEmail() == null) throw new UnauthorisedException("Cannot login user.Facebook account did not provide email"); user.setEmail(profile.getEmail()); + user.setId(profile.getId()); user.setIsVerified(profile.isVerified()); user.setName(profile.getName()); user.setProvider(TokenValidatorFactoryImpl.LoginProvider.FACEBOOK); diff --git a/dmp-backend/src/main/java/eu/eudat/security/validators/google/GoogleTokenValidator.java b/dmp-backend/src/main/java/eu/eudat/security/validators/google/GoogleTokenValidator.java index 0cacc37bc..66c62f99c 100644 --- a/dmp-backend/src/main/java/eu/eudat/security/validators/google/GoogleTokenValidator.java +++ b/dmp-backend/src/main/java/eu/eudat/security/validators/google/GoogleTokenValidator.java @@ -47,11 +47,11 @@ public class GoogleTokenValidator implements TokenValidator { @Override public eu.eudat.models.security.Principal validateToken(LoginInfo credentials) throws NonValidTokenException, IOException, GeneralSecurityException { - GoogleIdToken idToken = this.verifyUserAndGetUser(credentials.getTicket()); Payload payload = idToken.getPayload(); LoginProviderUser user = new LoginProviderUser(); user.setSecret(credentials.getTicket()); + user.setId( payload.getSubject()); user.setProvider(TokenValidatorFactoryImpl.LoginProvider.GOOGLE); user.setName((String) payload.get("name")); user.setEmail(payload.getEmail()); diff --git a/dmp-backend/src/main/java/eu/eudat/security/validators/linkedin/LinkedInTokenValidator.java b/dmp-backend/src/main/java/eu/eudat/security/validators/linkedin/LinkedInTokenValidator.java index 25ff2f00a..7dbd38c76 100644 --- a/dmp-backend/src/main/java/eu/eudat/security/validators/linkedin/LinkedInTokenValidator.java +++ b/dmp-backend/src/main/java/eu/eudat/security/validators/linkedin/LinkedInTokenValidator.java @@ -47,6 +47,7 @@ public class LinkedInTokenValidator implements TokenValidator { if (linkedInProfile.getEmailAddress() == null) throw new UnauthorisedException("Cannot login user.LinkedIn account did not provide email"); user.setEmail(linkedInProfile.getEmailAddress()); + user.setId(linkedInProfile.getId()); user.setIsVerified(true); //TODO user.setName(linkedInProfile.getFirstName() + " " + linkedInProfile.getLastName()); user.setProvider(TokenValidatorFactoryImpl.LoginProvider.LINKEDIN); diff --git a/dmp-backend/src/main/java/eu/eudat/security/validators/twitter/TwitterTokenValidator.java b/dmp-backend/src/main/java/eu/eudat/security/validators/twitter/TwitterTokenValidator.java index b7cd51d0d..c643aa205 100644 --- a/dmp-backend/src/main/java/eu/eudat/security/validators/twitter/TwitterTokenValidator.java +++ b/dmp-backend/src/main/java/eu/eudat/security/validators/twitter/TwitterTokenValidator.java @@ -54,6 +54,7 @@ public class TwitterTokenValidator implements TokenValidator { throw new UnauthorisedException("Cannot login user.Twitter account did not provide email"); user.setEmail((String) values.get("email")); user.setIsVerified(true); //TODO + user.setId(""+profile.getId()); user.setName(profile.getName()); user.setProvider(TokenValidatorFactoryImpl.LoginProvider.TWITTER); user.setSecret(finalOauthToken.getValue()); diff --git a/dmp-backend/src/main/java/eu/eudat/services/AuthenticationService.java b/dmp-backend/src/main/java/eu/eudat/services/AuthenticationService.java index c31aa16ff..06f8598ea 100644 --- a/dmp-backend/src/main/java/eu/eudat/services/AuthenticationService.java +++ b/dmp-backend/src/main/java/eu/eudat/services/AuthenticationService.java @@ -91,12 +91,21 @@ public class AuthenticationService { public Principal Touch(LoginProviderUser profile) { UserInfoCriteria criteria = new UserInfoCriteria(); criteria.setEmail(profile.getEmail()); + UserInfo userInfo = apiContext.getDatabaseRepository().getUserInfoDao().asQueryable().withHint("userInfo").where((builder, root) -> builder.equal(root.get("email"), profile.getEmail())).getSingleOrDefault(); + if(userInfo == null){ + Optional optionalCredential = Optional.ofNullable(apiContext.getDatabaseRepository().getCredentialDao() + .asQueryable().withHint("credentialUserInfo") + .where((builder, root) -> builder.and(builder.equal(root.get("provider"), profile.getProvider().getValue()), builder.equal(root.get("externalId"), profile.getId()))) + .getSingleOrDefault()); + userInfo = optionalCredential.map(Credential::getUserInfo).orElse(null); + } + final Credential credential = this.apiContext.getBuilderFactory().getBuilder(CredentialBuilder.class) .id(UUID.randomUUID()).creationTime(new Date()).status(1) .lastUpdateTime(new Date()).provider((int) profile.getProvider().getValue()) - .secret(profile.getSecret()) + .secret(profile.getSecret()).externalId(profile.getId()) .build(); if (userInfo == null) { diff --git a/dmp-backend/src/main/resources/application.properties b/dmp-backend/src/main/resources/application.properties index 61bbe6dd2..df8f7db46 100644 --- a/dmp-backend/src/main/resources/application.properties +++ b/dmp-backend/src/main/resources/application.properties @@ -49,4 +49,10 @@ spring.profiles.active=devel ########################Persistence/Hibernate/Connection pool#################### autouser.root.email=root@dmp.com autouser.root.password=root -autouser.root.username=root \ No newline at end of file +autouser.root.username=root +################################################################################# +b2access.externallogin.user_info_url = https://unity.eudat-aai.fz-juelich.de:443/oauth2/userinfo +b2access.externallogin.access_token_url = https://unity.eudat-aai.fz-juelich.de:443/oauth2/token +b2access.externallogin.redirect_uri = http://dmp.eudat.org:4200/api/oauth/authorized/b2access +b2access.externallogin.clientid = eudatdmptool +b2access.externallogin.clientSecret = A3b*1*92 \ No newline at end of file diff --git a/dmp-frontend/src/app/app-routing.module.ts b/dmp-frontend/src/app/app-routing.module.ts index 1cfc342eb..2c15fc190 100644 --- a/dmp-frontend/src/app/app-routing.module.ts +++ b/dmp-frontend/src/app/app-routing.module.ts @@ -6,6 +6,7 @@ import { HomepageComponent } from './homepage/homepage.component'; import { AuthGuard } from './guards/auth.guard'; import { LoginComponent } from './user-management/login/login.component'; import { WelcomepageComponent } from '@app/welcomepage/welcomepage.component'; +import { B2AccessLoginComponent } from './user-management/login/b2access/b2access-login.component'; const appRoutes: Routes = [ { path: 'datasets', loadChildren: './datasets/dataset.module#DatasetModule', canActivate: [AuthGuard] }, @@ -17,7 +18,8 @@ const appRoutes: Routes = [ { path: "unauthorized", loadChildren: './unauthorized/unauthorized.module#UnauthorizedModule' }, { path: "users", loadChildren: './users/users.module#UsersModule' }, { path: "datasetsProfiles", loadChildren: './datasets-admin-listing/dataset-admin.module#DatasetAdminModule' }, - { path: "welcome", component: WelcomepageComponent } + { path: "welcome", component: WelcomepageComponent }, + { path: "api/oauth/authorized/b2access", component: B2AccessLoginComponent } ]; @NgModule({ diff --git a/dmp-frontend/src/app/app.module.ts b/dmp-frontend/src/app/app.module.ts index 1855acec3..1cb7fc964 100644 --- a/dmp-frontend/src/app/app.module.ts +++ b/dmp-frontend/src/app/app.module.ts @@ -33,6 +33,7 @@ import { DatasetProfileModule } from './dataset-profile-form/dataset-profile.mod import { WelcomepageComponent } from '@app/welcomepage/welcomepage.component'; import { HelpContentService } from './services/help-content/help-content.service'; import { HelpContentComponent } from './help-content/help-content.component'; +import { B2AccessLoginComponent } from './user-management/login/b2access/b2access-login.component'; @NgModule({ declarations: [ @@ -40,7 +41,8 @@ import { HelpContentComponent } from './help-content/help-content.component'; PageNotFoundComponent, HomepageComponent, WelcomepageComponent, - HelpContentComponent + HelpContentComponent, + B2AccessLoginComponent ], imports: [ BrowserModule, @@ -53,7 +55,8 @@ import { HelpContentComponent } from './help-content/help-content.component'; LoginOptions.googleOauth, LoginOptions.nativeLogin, LoginOptions.linkedInOauth, - LoginOptions.twitterOauth + LoginOptions.twitterOauth, + LoginOptions.b2Access ], facebookConfiguration: { clientId: "110586756143149" }, googleConfiguration: { clientId: '524432312250-sc9qsmtmbvlv05r44onl6l93ia3k9deo.apps.googleusercontent.com' }, @@ -64,7 +67,14 @@ import { HelpContentComponent } from './help-content/help-content.component'; accessTokenUri: "https://www.linkedin.com/oauth/v2/accessToken", clientSecret: "2OCO9e3wKylW05Tt" }, - twitterConfiguration: { clientId: "HiR4hQH9HNubKC5iKQy0l4mAZ", oauthUrl: "https://api.twitter.com/oauth/authenticate" } + twitterConfiguration: { clientId: "HiR4hQH9HNubKC5iKQy0l4mAZ", oauthUrl: "https://api.twitter.com/oauth/authenticate" }, + b2accessConfiguration: { + clientId: "eudatdmptool", + clientSecret: "A3b*1*92", + oauthUrl: "https://unity.eudat-aai.fz-juelich.de/oauth2-as/oauth2-authz", + redirectUri: "http://dmp.eudat.org:4200/api/oauth/authorized/b2access", + accessTokenUri: "https://unity.eudat-aai.fz-juelich.de:443/oauth2/token" + } }), HttpModule, HttpClientModule, diff --git a/dmp-frontend/src/app/models/login/LoginInfo.ts b/dmp-frontend/src/app/models/login/LoginInfo.ts index 6e18cdf70..8fc2a4114 100644 --- a/dmp-frontend/src/app/models/login/LoginInfo.ts +++ b/dmp-frontend/src/app/models/login/LoginInfo.ts @@ -2,8 +2,10 @@ export enum LoginProviders { Google = 1, Facebook = 2, Twitter = 3, - LinkedIn = 4 -} + LinkedIn = 4, + NativeLogin = 5, + B2Accesss = 6 +} export class LoginInfo { public ticket: string; diff --git a/dmp-frontend/src/app/shared/components/navigation/navigation.component.html b/dmp-frontend/src/app/shared/components/navigation/navigation.component.html index 61cbcb5fd..36bd8c1ce 100644 --- a/dmp-frontend/src/app/shared/components/navigation/navigation.component.html +++ b/dmp-frontend/src/app/shared/components/navigation/navigation.component.html @@ -1,23 +1,22 @@ - {{'NAV-BAR.TITLE' | translate}} -
- - - - - -
- -
- {{this.getPrincipalName()}} - -
- + {{'NAV-BAR.TITLE' | translate}} +
+ + + + + +
+ +
+ {{this.getPrincipalName()}} + +
+ + - -
\ No newline at end of file + + + \ No newline at end of file diff --git a/dmp-frontend/src/app/user-management/login/b2access/b2access-login.component.html b/dmp-frontend/src/app/user-management/login/b2access/b2access-login.component.html new file mode 100644 index 000000000..e69de29bb diff --git a/dmp-frontend/src/app/user-management/login/b2access/b2access-login.component.ts b/dmp-frontend/src/app/user-management/login/b2access/b2access-login.component.ts new file mode 100644 index 000000000..71dc64b9f --- /dev/null +++ b/dmp-frontend/src/app/user-management/login/b2access/b2access-login.component.ts @@ -0,0 +1,26 @@ +import { LoginService } from '../../utilties/login-service'; +import { Component, OnInit } from '@angular/core' +import { Router, ActivatedRoute, Params } from '@angular/router'; + +@Component({ + selector: 'b2access-login', + templateUrl: './b2access-login.component.html', +}) +export class B2AccessLoginComponent implements OnInit { + + constructor( + private router: Router, + private route: ActivatedRoute, + private loginService: LoginService + ) { + + } + + ngOnInit(): void { + this.route.queryParams.subscribe((data: any) => { + if (!data["code"]) this.loginService.b2AccessGetAuthCode() + else this.loginService.b2AccessLogin(data["code"]) + }) + } + +} \ No newline at end of file diff --git a/dmp-frontend/src/app/user-management/login/login.component.html b/dmp-frontend/src/app/user-management/login/login.component.html index 9c7770711..879ec05b8 100644 --- a/dmp-frontend/src/app/user-management/login/login.component.html +++ b/dmp-frontend/src/app/user-management/login/login.component.html @@ -19,6 +19,14 @@ + + +
+
@@ -37,7 +45,7 @@
diff --git a/dmp-frontend/src/app/user-management/login/login.component.ts b/dmp-frontend/src/app/user-management/login/login.component.ts index dcaf945d6..816f4a342 100644 --- a/dmp-frontend/src/app/user-management/login/login.component.ts +++ b/dmp-frontend/src/app/user-management/login/login.component.ts @@ -45,6 +45,11 @@ export class LoginComponent implements OnInit { public nativeLogin() { this.loginService.nativeLogin(this.credential); } + + public b2AccessLogin() { + return this.loginService.b2AccessInitialiseLogin(); + } + public hasFacebookOauth(): boolean { return this.loginService.hasProvider(LoginOptions.facebookOauth); } @@ -65,4 +70,8 @@ export class LoginComponent implements OnInit { return this.loginService.hasProvider(LoginOptions.nativeLogin); } + public hasB2AccessOauth(): boolean { + return this.loginService.hasProvider(LoginOptions.b2Access); + } + } \ No newline at end of file diff --git a/dmp-frontend/src/app/user-management/utilties/LoginOptions.ts b/dmp-frontend/src/app/user-management/utilties/LoginOptions.ts index 51187d8ba..b2a328fc0 100644 --- a/dmp-frontend/src/app/user-management/utilties/LoginOptions.ts +++ b/dmp-frontend/src/app/user-management/utilties/LoginOptions.ts @@ -4,5 +4,6 @@ export enum LoginOptions{ twitterOauth = 3, googleOauth = 4, nativeLogin = 5, - all = 6 + b2Access = 6, + all = 7, } \ No newline at end of file diff --git a/dmp-frontend/src/app/user-management/utilties/LoginProviderConfiguration.ts b/dmp-frontend/src/app/user-management/utilties/LoginProviderConfiguration.ts index 52b67eeb0..38a9f4745 100644 --- a/dmp-frontend/src/app/user-management/utilties/LoginProviderConfiguration.ts +++ b/dmp-frontend/src/app/user-management/utilties/LoginProviderConfiguration.ts @@ -17,4 +17,11 @@ export class LinkedInConfiguration extends LoginProviderConfiguration { public redirectUri: string public accessTokenUri: string public clientSecret: string +} + +export class B2AccessConfiguration extends LoginProviderConfiguration { + public oauthUrl: string + public redirectUri: string + public accessTokenUri: string + public clientSecret: string } \ No newline at end of file diff --git a/dmp-frontend/src/app/user-management/utilties/LoginServiceConfiguration.ts b/dmp-frontend/src/app/user-management/utilties/LoginServiceConfiguration.ts index bb251a8b3..05aaa5e1d 100644 --- a/dmp-frontend/src/app/user-management/utilties/LoginServiceConfiguration.ts +++ b/dmp-frontend/src/app/user-management/utilties/LoginServiceConfiguration.ts @@ -3,6 +3,7 @@ import { GoogleLoginConfiguration, LinkedInConfiguration, TwitterLoginConfiguration, + B2AccessConfiguration, } from './LoginProviderConfiguration'; import { LoginOptions } from './LoginOptions'; export class LoginServiceConfiguration { @@ -11,4 +12,5 @@ export class LoginServiceConfiguration { public googleConfiguration?: GoogleLoginConfiguration; public twitterConfiguration?: TwitterLoginConfiguration; public linkedInConfiguration?: LinkedInConfiguration; + public b2accessConfiguration?: B2AccessConfiguration; } \ No newline at end of file diff --git a/dmp-frontend/src/app/user-management/utilties/login-service.ts b/dmp-frontend/src/app/user-management/utilties/login-service.ts index 9aa4930c2..d1d0cd646 100644 --- a/dmp-frontend/src/app/user-management/utilties/login-service.ts +++ b/dmp-frontend/src/app/user-management/utilties/login-service.ts @@ -56,6 +56,7 @@ export class LoginService { case LoginOptions.googleOauth: return this.hasAllRequiredFieldsConfigured(this.config.googleConfiguration) case LoginOptions.linkedInOauth: return this.hasAllRequiredFieldsConfigured(this.config.linkedInConfiguration); case LoginOptions.twitterOauth: return this.hasAllRequiredFieldsConfigured(this.config.twitterConfiguration); + case LoginOptions.b2Access: return this.hasAllRequiredFieldsConfigured(this.config.b2accessConfiguration); case LoginOptions.nativeLogin: return true; default: throw new Error("Unsupported Provider Type") } @@ -169,6 +170,31 @@ export class LoginService { ) } + /* + * B2ACCESS LOG IN + */ + + public b2AccessInitialiseLogin() { + this.router.navigate(["/api/oauth/authorized/b2access"]) + } + + public b2AccessGetAuthCode() { + window.location.href = this.config.b2accessConfiguration.oauthUrl + "?response_type=code&client_id=" + this.config.b2accessConfiguration.clientId + "&redirect_uri=" + this.config.b2accessConfiguration.redirectUri + "&state=987654321&scope=USER_PROFILE" + } + + public b2AccessLogin(code: String) { + let headers = new HttpHeaders(); + headers = headers.set('Content-Type', 'application/json'); + headers = headers.set('Accept', 'application/json'); + this.httpClient.post(HostConfiguration.Server + "auth/b2AccessRequestToken", { code: code }, { headers: headers }) + .subscribe((data: any) => { + this.authService.login({ ticket: data.payload.accessToken, provider: LoginProviders.B2Accesss, data: null }).subscribe( + res => this.onLogInSuccess(res), + error => this.onLogInError(error) + ) + }) + } + /* * NATIVE LOGIN diff --git a/dmp-frontend/src/index.html b/dmp-frontend/src/index.html index eb9b05831..59c74d02a 100644 --- a/dmp-frontend/src/index.html +++ b/dmp-frontend/src/index.html @@ -2,6 +2,8 @@ + + @@ -12,14 +14,14 @@ - - +