diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/Login.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/Login.java index 78304aa20..16af6f14f 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/Login.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/Login.java @@ -22,6 +22,9 @@ 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; import eu.eudat.logic.security.validators.twitter.TwitterTokenValidator; +import eu.eudat.logic.security.validators.zenodo.ZenodoTokenValidator; +import eu.eudat.logic.security.validators.zenodo.helpers.ZenodoRequest; +import eu.eudat.logic.security.validators.zenodo.helpers.ZenodoResponseToken; import eu.eudat.logic.services.operations.authentication.AuthenticationService; import eu.eudat.models.data.helpers.responses.ResponseItem; import eu.eudat.models.data.login.Credentials; @@ -54,28 +57,34 @@ public class Login { private LinkedInTokenValidator linkedInTokenValidator; private OpenAIRETokenValidator openAIRETokenValidator; private ConfigurableProviderTokenValidator configurableProviderTokenValidator; + private ZenodoTokenValidator zenodoTokenValidator; private ConfigLoader configLoader; // private Logger logger; private UserManager userManager; + @Autowired - public Login(CustomAuthenticationProvider customAuthenticationProvider, AuthenticationService nonVerifiedUserAuthenticationService, - TwitterTokenValidator twitterTokenValidator, LinkedInTokenValidator linkedInTokenValidator, B2AccessTokenValidator b2AccessTokenValidator, - ORCIDTokenValidator orcidTokenValidator, OpenAIRETokenValidator openAIRETokenValidator, ConfigurableProviderTokenValidator configurableProviderTokenValidator, ConfigLoader configLoader, UserManager userManager/*, Logger logger*/) { + public Login(CustomAuthenticationProvider customAuthenticationProvider, + AuthenticationService nonVerifiedUserAuthenticationService, TwitterTokenValidator twitterTokenValidator, + B2AccessTokenValidator b2AccessTokenValidator, ORCIDTokenValidator orcidTokenValidator, + LinkedInTokenValidator linkedInTokenValidator, OpenAIRETokenValidator openAIRETokenValidator, + ConfigurableProviderTokenValidator configurableProviderTokenValidator, ZenodoTokenValidator zenodoTokenValidator, + ConfigLoader configLoader, UserManager userManager) { this.customAuthenticationProvider = customAuthenticationProvider; this.nonVerifiedUserAuthenticationService = nonVerifiedUserAuthenticationService; this.twitterTokenValidator = twitterTokenValidator; - this.linkedInTokenValidator = linkedInTokenValidator; this.b2AccessTokenValidator = b2AccessTokenValidator; this.orcidTokenValidator = orcidTokenValidator; + this.linkedInTokenValidator = linkedInTokenValidator; this.openAIRETokenValidator = openAIRETokenValidator; this.configurableProviderTokenValidator = configurableProviderTokenValidator; + this.zenodoTokenValidator = zenodoTokenValidator; this.configLoader = configLoader; -// this.logger = logger; this.userManager = userManager; } + @Transactional @RequestMapping(method = RequestMethod.POST, value = {"/externallogin"}, consumes = "application/json", produces = "application/json") public @ResponseBody @@ -128,6 +137,12 @@ public class Login { return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(this.configurableProviderTokenValidator.getAccessToken(configurableProviderRequest)).status(ApiMessageCode.NO_MESSAGE)); } + @RequestMapping(method = RequestMethod.POST, value = {"/zenodoRequestToken"}, produces = "application/json", consumes = "application/json") + public @ResponseBody + ResponseEntity> ZenodoRequestToken(@RequestBody ZenodoRequest zenodoRequest) { + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(this.zenodoTokenValidator.getAccessToken(zenodoRequest)).status(ApiMessageCode.NO_MESSAGE)); + } + @RequestMapping(method = RequestMethod.POST, value = {"/me"}, consumes = "application/json", produces = "application/json") public @ResponseBody ResponseEntity> authMe(Principal principal) throws NullEmailException { diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/builders/model/models/PrincipalBuilder.java b/dmp-backend/web/src/main/java/eu/eudat/logic/builders/model/models/PrincipalBuilder.java index 133d5e179..35ce34035 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/builders/model/models/PrincipalBuilder.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/builders/model/models/PrincipalBuilder.java @@ -4,6 +4,7 @@ import eu.eudat.logic.builders.Builder; import eu.eudat.models.data.security.Principal; import eu.eudat.types.Authorities; +import java.time.Instant; import java.util.Date; import java.util.Set; import java.util.UUID; @@ -22,6 +23,8 @@ public class PrincipalBuilder extends Builder { private String culture; private String language; private String timezone; + private String zenodoToken; + private Instant zenodoDuration; public PrincipalBuilder id(UUID id) { this.id = id; @@ -68,6 +71,16 @@ public class PrincipalBuilder extends Builder { return this; } + public PrincipalBuilder zenodoToken(String zenodoToken) { + this.zenodoToken = zenodoToken; + return this; + } + + public PrincipalBuilder zenodoDuration(Instant zenodoDuration) { + this.zenodoDuration = zenodoDuration; + return this; + } + @Override public Principal build() { Principal principal = new Principal(); @@ -80,6 +93,8 @@ public class PrincipalBuilder extends Builder { principal.setCulture(culture); principal.setLanguage(language); principal.setTimezone(timezone); + principal.setZenodoToken(zenodoToken); + principal.setZenodoDuration(zenodoDuration); return principal; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java index af7035943..2d533b4f4 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java @@ -1599,6 +1599,7 @@ public class DataManagementPlanManager { if (dmp.getDoi() != null) throw new Exception("DMP already has a DOI"); + String zenodoToken = principal.getZenodoToken() != null && !principal.getZenodoToken().isEmpty() ? principal.getZenodoToken() : this.environment.getProperty("zenodo.access_token"); // First step, post call to Zenodo, to create the entry. RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); @@ -1623,34 +1624,34 @@ public class DataManagementPlanManager { String previousDOI = this.getPreviousDOI(dmp.getGroupId(), dmp.getId()); try { if (previousDOI == null) { - String createUrl = this.environment.getProperty("zenodo.url") + "deposit/depositions" + "?access_token=" + this.environment.getProperty("zenodo.access_token"); + String createUrl = this.environment.getProperty("zenodo.url") + "deposit/depositions" + "?access_token=" + zenodoToken; createResponse = restTemplate.postForEntity(createUrl, request, Map.class).getBody(); links = (LinkedHashMap) createResponse.get("links"); } else { //It requires more than one step to create a new version //First, get the deposit related to the concept DOI - String listUrl = this.environment.getProperty("zenodo.url") + "deposit/depositions" + "?q=conceptdoi:\"" + previousDOI + "\"&access_token=" + this.environment.getProperty("zenodo.access_token"); + String listUrl = this.environment.getProperty("zenodo.url") + "deposit/depositions" + "?q=conceptdoi:\"" + previousDOI + "\"&access_token=" + zenodoToken; ResponseEntity listResponses = restTemplate.getForEntity(listUrl, Map[].class); createResponse = listResponses.getBody()[0]; links = (LinkedHashMap) createResponse.get("links"); //Second, make the new version (not in the links?) - String newVersionUrl = links.get("self") + "/actions/newversion" + "?access_token=" + this.environment.getProperty("zenodo.access_token"); + String newVersionUrl = links.get("self") + "/actions/newversion" + "?access_token=" + zenodoToken; createResponse = restTemplate.postForObject(newVersionUrl, null, Map.class); links = (LinkedHashMap) createResponse.get("links"); //Third, get the new deposit - String latestDraftUrl = links.get("latest_draft") + "?access_token=" + this.environment.getProperty("zenodo.access_token"); + String latestDraftUrl = links.get("latest_draft") + "?access_token=" + zenodoToken; createResponse = restTemplate.getForObject(latestDraftUrl, Map.class); links = (LinkedHashMap) createResponse.get("links"); //At this point it might fail to perform the next requests so enclose them with try catch try { //Forth, update the new deposit's metadata - String updateUrl = links.get("self") + "?access_token=" + this.environment.getProperty("zenodo.access_token"); + String updateUrl = links.get("self") + "?access_token=" + zenodoToken; restTemplate.put(updateUrl, request); //And finally remove pre-existing files from it - String fileListUrl = links.get("self") + "/files" + "?access_token=" + this.environment.getProperty("zenodo.access_token"); + String fileListUrl = links.get("self") + "/files" + "?access_token=" + zenodoToken; ResponseEntity fileListResponse = restTemplate.getForEntity(fileListUrl, Map[].class); for (Map file : fileListResponse.getBody()) { - String fileDeleteUrl = links.get("self") + "/files/" + file.get("id") + "?access_token=" + this.environment.getProperty("zenodo.access_token"); + String fileDeleteUrl = links.get("self") + "/files/" + file.get("id") + "?access_token=" + zenodoToken; restTemplate.delete(fileDeleteUrl); } }catch (Exception e) { diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/Zenodo/ZenodoCustomProvider.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/Zenodo/ZenodoCustomProvider.java new file mode 100644 index 000000000..b14c3a963 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/Zenodo/ZenodoCustomProvider.java @@ -0,0 +1,8 @@ +package eu.eudat.logic.security.customproviders.Zenodo; + +import eu.eudat.logic.security.validators.orcid.helpers.ORCIDResponseToken; +import eu.eudat.logic.security.validators.zenodo.helpers.ZenodoResponseToken; + +public interface ZenodoCustomProvider { + ZenodoResponseToken getAccessToken(String code, String clientId, String clientSecret, String redirectUri); +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/Zenodo/ZenodoCustomProviderImpl.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/Zenodo/ZenodoCustomProviderImpl.java new file mode 100644 index 000000000..b527af2fe --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/Zenodo/ZenodoCustomProviderImpl.java @@ -0,0 +1,72 @@ +package eu.eudat.logic.security.customproviders.Zenodo; + +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.eudat.logic.security.validators.orcid.helpers.ORCIDResponseToken; +import eu.eudat.logic.security.validators.zenodo.helpers.ZenodoResponseToken; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; + +@Component("ZenodoCustomProvider") +public class ZenodoCustomProviderImpl implements ZenodoCustomProvider { + private static final Logger logger = LoggerFactory.getLogger(ZenodoCustomProviderImpl.class); + + private Environment environment; + + @Autowired + public ZenodoCustomProviderImpl(Environment environment) { + this.environment = environment; + } + + @Override + public ZenodoResponseToken getAccessToken(String code, String clientId, String clientSecret, String redirectUri) { + RestTemplate restTemplate = new RestTemplate(); + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + + MultiValueMap map = new LinkedMultiValueMap<>(); + map.add("client_id", clientId); + map.add("client_secret", clientSecret); + map.add("grant_type", "authorization_code"); + map.add("code", code); + map.add("redirect_uri", redirectUri); + HttpEntity> request = new HttpEntity<>(map, headers); + + try { + Map values = restTemplate.postForObject(this.environment.getProperty("zenodo.login.access_token_url"), request, Map.class); + ZenodoResponseToken zenodoResponseToken = new ZenodoResponseToken(); + Map user = (Map) values.get("user"); + zenodoResponseToken.setUserId((String) user.get("id")); + zenodoResponseToken.setEmail((String) user.get("email")); + zenodoResponseToken.setExpiresIn((Integer) values.get("expires_in")); + zenodoResponseToken.setAccessToken((String) values.get("access_token")); + + return zenodoResponseToken; + } catch (HttpClientErrorException ex) { + logger.error(ex.getResponseBodyAsString(), ex); + } + + return null; + } + + private HttpHeaders createBearerAuthHeaders(String accessToken) { + return new HttpHeaders() {{ + String authHeader = "Bearer " + accessToken; + set("Authorization", authHeader); + }}; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/Zenodo/ZenodoUser.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/Zenodo/ZenodoUser.java new file mode 100644 index 000000000..cbcb95928 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/Zenodo/ZenodoUser.java @@ -0,0 +1,49 @@ +package eu.eudat.logic.security.customproviders.Zenodo; + +import java.util.Map; + +public class ZenodoUser { + private String userId; + private String email; + private String accessToken; + private Integer expiresIn; + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getEmail() { + return email; + } + public void setEmail(String email) { + this.email = email; + } + + 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; + } + + public ZenodoUser getZenodoUser(Object data) { + this.userId = (String) ((Map) data).get("userId"); + this.email = (String) ((Map) data).get("email"); + this.accessToken = (String) ((Map) data).get("accessToken"); + this.expiresIn = (Integer) ((Map) data).get("expiresIn"); + return this; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/TokenValidatorFactoryImpl.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/TokenValidatorFactoryImpl.java index 9b594c96e..c079d24a5 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/TokenValidatorFactoryImpl.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/TokenValidatorFactoryImpl.java @@ -6,6 +6,7 @@ import eu.eudat.logic.security.customproviders.ConfigurableProvider.Configurable import eu.eudat.logic.security.customproviders.LinkedIn.LinkedInCustomProvider; import eu.eudat.logic.security.customproviders.ORCID.ORCIDCustomProvider; import eu.eudat.logic.security.customproviders.OpenAIRE.OpenAIRECustomProvider; +import eu.eudat.logic.security.customproviders.Zenodo.ZenodoCustomProvider; import eu.eudat.logic.security.validators.b2access.B2AccessTokenValidator; import eu.eudat.logic.security.validators.configurableProvider.ConfigurableProviderTokenValidator; import eu.eudat.logic.security.validators.facebook.FacebookTokenValidator; @@ -14,6 +15,7 @@ import eu.eudat.logic.security.validators.linkedin.LinkedInTokenValidator; import eu.eudat.logic.security.validators.openaire.OpenAIRETokenValidator; import eu.eudat.logic.security.validators.orcid.ORCIDTokenValidator; import eu.eudat.logic.security.validators.twitter.TwitterTokenValidator; +import eu.eudat.logic.security.validators.zenodo.ZenodoTokenValidator; import eu.eudat.logic.services.operations.authentication.AuthenticationService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; @@ -23,7 +25,7 @@ import org.springframework.stereotype.Service; @Service("tokenValidatorFactory") public class TokenValidatorFactoryImpl implements TokenValidatorFactory { public enum LoginProvider { - GOOGLE(1), FACEBOOK(2), TWITTER(3), LINKEDIN(4), NATIVELOGIN(5), B2_ACCESS(6), ORCID(7), OPENAIRE(8), CONFIGURABLE(9); + GOOGLE(1), FACEBOOK(2), TWITTER(3), LINKEDIN(4), NATIVELOGIN(5), B2_ACCESS(6), ORCID(7), OPENAIRE(8), CONFIGURABLE(9), ZENODO(10); private int value; @@ -55,6 +57,8 @@ public class TokenValidatorFactoryImpl implements TokenValidatorFactory { return OPENAIRE; case 9: return CONFIGURABLE; + case 10: + return ZENODO; default: throw new RuntimeException("Unsupported LoginProvider"); } @@ -69,12 +73,15 @@ public class TokenValidatorFactoryImpl implements TokenValidatorFactory { private OpenAIRECustomProvider openAIRECustomProvider; private ConfigurableProviderCustomProvider configurableProviderCustomProvider; private ConfigLoader configLoader; + private ZenodoCustomProvider zenodoCustomProvider; @Autowired public TokenValidatorFactoryImpl( Environment environment, AuthenticationService nonVerifiedUserAuthenticationService, B2AccessCustomProvider b2AccessCustomProvider, - ORCIDCustomProvider orcidCustomProvider, LinkedInCustomProvider linkedInCustomProvider, OpenAIRECustomProvider openAIRECustomProvider, ConfigurableProviderCustomProvider configurableProviderCustomProvider, ConfigLoader configLoader) { + ORCIDCustomProvider orcidCustomProvider, LinkedInCustomProvider linkedInCustomProvider, OpenAIRECustomProvider openAIRECustomProvider, + ConfigurableProviderCustomProvider configurableProviderCustomProvider, ConfigLoader configLoader, + ZenodoCustomProvider zenodoCustomProvider) { this.environment = environment; this.nonVerifiedUserAuthenticationService = nonVerifiedUserAuthenticationService; this.b2AccessCustomProvider = b2AccessCustomProvider; @@ -83,6 +90,7 @@ public class TokenValidatorFactoryImpl implements TokenValidatorFactory { this.openAIRECustomProvider = openAIRECustomProvider; this.configurableProviderCustomProvider = configurableProviderCustomProvider; this.configLoader = configLoader; + this.zenodoCustomProvider = zenodoCustomProvider; } public TokenValidator getProvider(LoginProvider provider) { @@ -103,6 +111,8 @@ public class TokenValidatorFactoryImpl implements TokenValidatorFactory { return new OpenAIRETokenValidator(this.environment, this.nonVerifiedUserAuthenticationService, this.openAIRECustomProvider); case CONFIGURABLE: return new ConfigurableProviderTokenValidator(this.configurableProviderCustomProvider, this.nonVerifiedUserAuthenticationService, this.configLoader); + case ZENODO: + return new ZenodoTokenValidator(this.environment, this.nonVerifiedUserAuthenticationService, this.zenodoCustomProvider); default: throw new RuntimeException("Login Provider Not Implemented"); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/zenodo/ZenodoTokenValidator.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/zenodo/ZenodoTokenValidator.java new file mode 100644 index 000000000..2708a96b2 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/zenodo/ZenodoTokenValidator.java @@ -0,0 +1,57 @@ +package eu.eudat.logic.security.validators.zenodo; + +import eu.eudat.exceptions.security.NonValidTokenException; +import eu.eudat.exceptions.security.NullEmailException; +import eu.eudat.logic.security.customproviders.ORCID.ORCIDCustomProvider; +import eu.eudat.logic.security.customproviders.ORCID.ORCIDUser; +import eu.eudat.logic.security.customproviders.Zenodo.ZenodoCustomProvider; +import eu.eudat.logic.security.customproviders.Zenodo.ZenodoUser; +import eu.eudat.logic.security.validators.TokenValidator; +import eu.eudat.logic.security.validators.zenodo.helpers.ZenodoRequest; +import eu.eudat.logic.security.validators.zenodo.helpers.ZenodoResponseToken; +import eu.eudat.logic.services.operations.authentication.AuthenticationService; +import eu.eudat.models.data.login.LoginInfo; +import eu.eudat.models.data.loginprovider.LoginProviderUser; +import eu.eudat.models.data.security.Principal; +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; + +@Component("zenodoTokenValidator") +public class ZenodoTokenValidator implements TokenValidator { + + private ZenodoCustomProvider zenodoCustomProvider; + private Environment environment; + private AuthenticationService nonVerifiedUserAuthenticationService; + + @Autowired + public ZenodoTokenValidator(Environment environment, AuthenticationService nonVerifiedUserAuthenticationService, ZenodoCustomProvider zenodoCustomProvider) { + this.environment = environment; + this.nonVerifiedUserAuthenticationService = nonVerifiedUserAuthenticationService; + this.zenodoCustomProvider = zenodoCustomProvider; + } + + @Override + public Principal validateToken(LoginInfo credentials) throws NonValidTokenException, IOException, GeneralSecurityException, NullEmailException { + ZenodoUser zenodoUser = new ZenodoUser().getZenodoUser(credentials.getData()); + LoginProviderUser user = new LoginProviderUser(); + user.setId(zenodoUser.getUserId()); + user.setName(zenodoUser.getEmail()); + user.setEmail(zenodoUser.getEmail()); + user.setZenodoId(zenodoUser.getAccessToken()); + user.setZenodoExpire(zenodoUser.getExpiresIn()); + user.setProvider(credentials.getProvider()); + user.setSecret(credentials.getTicket()); + return this.nonVerifiedUserAuthenticationService.Touch(user); + } + + public ZenodoResponseToken getAccessToken(ZenodoRequest zenodoRequest) { + return this.zenodoCustomProvider.getAccessToken(zenodoRequest.getCode() + , this.environment.getProperty("zenodo.login.client_id") + , this.environment.getProperty("zenodo.login.client_secret") + , this.environment.getProperty("zenodo.login.redirect_uri")); + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/zenodo/helpers/ZenodoRequest.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/zenodo/helpers/ZenodoRequest.java new file mode 100644 index 000000000..c1f4ea32a --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/zenodo/helpers/ZenodoRequest.java @@ -0,0 +1,12 @@ +package eu.eudat.logic.security.validators.zenodo.helpers; + +public class ZenodoRequest { + private String code; + + public String getCode() { + return code; + } + public void setCode(String code) { + this.code = code; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/zenodo/helpers/ZenodoResponseToken.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/zenodo/helpers/ZenodoResponseToken.java new file mode 100644 index 000000000..5b437cdfb --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/zenodo/helpers/ZenodoResponseToken.java @@ -0,0 +1,38 @@ +package eu.eudat.logic.security.validators.zenodo.helpers; + +public class ZenodoResponseToken { + private String userId; + private String email; + private Integer expiresIn; + private String accessToken; + + public String getUserId() { + return userId; + } + public void setUserId(String userId) { + this.userId = userId; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public Integer getExpiresIn() { + return expiresIn; + } + + public void setExpiresIn(Integer expiresIn) { + this.expiresIn = expiresIn; + } + + public String getAccessToken() { + return accessToken; + } + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/AbstractAuthenticationService.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/AbstractAuthenticationService.java index cb7506ab6..fc09056fa 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/AbstractAuthenticationService.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/AbstractAuthenticationService.java @@ -20,6 +20,7 @@ import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.transaction.annotation.Transactional; +import java.time.Instant; import java.util.*; public abstract class AbstractAuthenticationService implements AuthenticationService { @@ -132,7 +133,7 @@ public abstract class AbstractAuthenticationService implements AuthenticationSer userInfo = this.apiContext.getOperationsContext().getBuilderFactory().getBuilder(UserInfoBuilder.class) .name(profile.getName()).verified_email(profile.getIsVerified()) .email(profile.getEmail()).created(new Date()).lastloggedin(new Date()) - .additionalinfo("{\"data\":{\"avatar\":{\"url\":\"" + profile.getAvatarUrl() + "\"}}}") + .additionalinfo("{\"data\":{\"avatar\":{\"url\":\"" + profile.getAvatarUrl() + "\"}},{\"zenodoToken\":\"" + profile.getZenodoId() + "\", \"expirationDate\": \"" + Instant.now().plusSeconds(profile.getZenodoExpire()).toEpochMilli() + "\"}") .authorization_level((short) 1).usertype((short) 1) .build(); @@ -149,7 +150,15 @@ public abstract class AbstractAuthenticationService implements AuthenticationSer } else { Map additionalInfo = userInfo.getAdditionalinfo() != null ? new JSONObject(userInfo.getAdditionalinfo()).toMap() : new HashMap<>(); - additionalInfo.put("avatarUrl", profile.getAvatarUrl()); + if (profile.getAvatarUrl() != null && !profile.getAvatarUrl().isEmpty() && !profile.getAvatarUrl().equals("null")) { + additionalInfo.put("avatarUrl", profile.getAvatarUrl()); + } + if (profile.getZenodoId() != null && !profile.getZenodoId().isEmpty() && !profile.getZenodoId().equals("null")) { + additionalInfo.put("zenodoToken", profile.getZenodoId()); + } + if (profile.getZenodoExpire() != null) { + additionalInfo.put("expirationDate", Instant.now().plusSeconds(profile.getZenodoExpire()).toEpochMilli()); + } userInfo.setLastloggedin(new Date()); userInfo.setAdditionalinfo(new JSONObject(additionalInfo).toString()); Set credentials = userInfo.getCredentials(); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/NonVerifiedUserEmailAuthenticationService.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/NonVerifiedUserEmailAuthenticationService.java index 62c5f60ae..159bbb235 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/NonVerifiedUserEmailAuthenticationService.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/NonVerifiedUserEmailAuthenticationService.java @@ -11,6 +11,7 @@ import eu.eudat.types.Authorities; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; +import java.time.Instant; import java.util.Date; import java.util.HashSet; import java.util.List; @@ -33,6 +34,18 @@ public class NonVerifiedUserEmailAuthenticationService extends AbstractAuthentic } catch (Exception e) { avatarUrl = ""; } + String zenodoToken; + try { + zenodoToken = user.getAdditionalinfo() != null ? new ObjectMapper().readTree(user.getAdditionalinfo()).get("zenodoToken").asText() : ""; + } catch (Exception e) { + zenodoToken = ""; + } + Instant zenodoDuration; + try { + zenodoDuration = user.getAdditionalinfo() != null ? Instant.ofEpochMilli(new ObjectMapper().readTree(user.getAdditionalinfo()).get("expirationDate").asLong()) : Instant.now(); + } catch (Exception e) { + zenodoDuration = Instant.now(); + } String culture; try { culture = user.getAdditionalinfo() != null ? new ObjectMapper().readTree(user.getAdditionalinfo()).get("culture").get("name").asText() : ""; @@ -58,6 +71,8 @@ public class NonVerifiedUserEmailAuthenticationService extends AbstractAuthentic .culture(culture) .language(language) .timezone(timezone) + .zenodoToken(zenodoToken) + .zenodoDuration(zenodoDuration) .build(); List userRoles = apiContext.getOperationsContext().getDatabaseRepository().getUserRoleDao().getUserRoles(user); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/VerifiedUserAuthenticationService.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/VerifiedUserAuthenticationService.java index acbb3dc86..c03e81934 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/VerifiedUserAuthenticationService.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/VerifiedUserAuthenticationService.java @@ -20,6 +20,7 @@ import org.json.JSONObject; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; +import java.time.Instant; import java.util.*; @@ -42,6 +43,18 @@ public class VerifiedUserAuthenticationService extends AbstractAuthenticationSer } catch (Exception e) { avatarUrl = ""; } + String zenodoToken; + try { + zenodoToken = user.getAdditionalinfo() != null ? new ObjectMapper().readTree(user.getAdditionalinfo()).get("zenodoToken").asText() : ""; + } catch (Exception e) { + zenodoToken = ""; + } + Instant zenodoDuration; + try { + zenodoDuration = user.getAdditionalinfo() != null ? Instant.ofEpochMilli(new ObjectMapper().readTree(user.getAdditionalinfo()).get("expirationDate").asLong()) : Instant.now(); + } catch (Exception e) { + zenodoDuration = Instant.now(); + } String culture; try { culture = user.getAdditionalinfo() != null ? new ObjectMapper().readTree(user.getAdditionalinfo()).get("culture").get("name").asText() : ""; @@ -67,6 +80,8 @@ public class VerifiedUserAuthenticationService extends AbstractAuthenticationSer .culture(culture) .language(language) .timezone(timezone) + .zenodoToken(zenodoToken) + .zenodoDuration(zenodoDuration) .build(); List userRoles = apiContext.getOperationsContext().getDatabaseRepository().getUserRoleDao().getUserRoles(user); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/loginprovider/LoginProviderUser.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/loginprovider/LoginProviderUser.java index e1b0a6d41..13f5b9371 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/loginprovider/LoginProviderUser.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/loginprovider/LoginProviderUser.java @@ -11,6 +11,8 @@ public class LoginProviderUser { private String avatarUrl; private boolean isVerified; private TokenValidatorFactoryImpl.LoginProvider provider; + private String zenodoId; + private Integer zenodoExpire; public String getId() { return id; @@ -67,4 +69,20 @@ public class LoginProviderUser { public void setAvatarUrl(String avatarUrl) { this.avatarUrl = avatarUrl; } + + public String getZenodoId() { + return zenodoId; + } + + public void setZenodoId(String zenodoId) { + this.zenodoId = zenodoId; + } + + public Integer getZenodoExpire() { + return zenodoExpire; + } + + public void setZenodoExpire(Integer zenodoExpire) { + this.zenodoExpire = zenodoExpire; + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/security/Principal.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/security/Principal.java index 5399b5466..f0e017e84 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/security/Principal.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/security/Principal.java @@ -3,6 +3,7 @@ package eu.eudat.models.data.security; import com.fasterxml.jackson.annotation.JsonIgnore; import eu.eudat.types.Authorities; +import java.time.Instant; import java.util.*; import java.util.stream.Collectors; @@ -17,6 +18,8 @@ public class Principal { private String culture; private String language; private String timezone; + private String zenodoToken; + private Instant zenodoDuration; public UUID getId() { return id; @@ -86,6 +89,22 @@ public class Principal { this.timezone = timezone; } + public String getZenodoToken() { + return zenodoToken; + } + + public void setZenodoToken(String zenodoToken) { + this.zenodoToken = zenodoToken; + } + + public Instant getZenodoDuration() { + return zenodoDuration; + } + + public void setZenodoDuration(Instant zenodoDuration) { + this.zenodoDuration = zenodoDuration; + } + @JsonIgnore public Set getAuthz() { return this.authorities; diff --git a/dmp-backend/web/src/main/resources/config/application-devel.properties b/dmp-backend/web/src/main/resources/config/application-devel.properties index 59d292b11..7051f6667 100644 --- a/dmp-backend/web/src/main/resources/config/application-devel.properties +++ b/dmp-backend/web/src/main/resources/config/application-devel.properties @@ -69,6 +69,10 @@ conf_email.subject=OpenDMP email confirmation #############ZENODO CONFIGURATIONS######### zenodo.url=https://sandbox.zenodo.org/api/ zenodo.access_token= +zenodo.login.access_token_url=https://sandbox.zenodo.org/oauth/token +zenodo.login.client_id= +zenodo.login.client_secret= +zenodo.login.redirect_uri=http://localhost:4200/login/external/zenodo #############CONTACT EMAIL CONFIGURATIONS######### contact_email.mail= diff --git a/dmp-frontend/src/app/core/common/enum/auth-provider.ts b/dmp-frontend/src/app/core/common/enum/auth-provider.ts index b8b70efd8..11c36b2a9 100644 --- a/dmp-frontend/src/app/core/common/enum/auth-provider.ts +++ b/dmp-frontend/src/app/core/common/enum/auth-provider.ts @@ -7,5 +7,6 @@ export enum AuthProvider { B2Access = 6, ORCID = 7, OpenAire = 8, - Configurable = 9 + Configurable = 9, + Zenodo = 10 } diff --git a/dmp-frontend/src/app/core/model/configuration-models/login-providers.model.ts b/dmp-frontend/src/app/core/model/configuration-models/login-providers.model.ts index 8c7751e2c..15474bdce 100644 --- a/dmp-frontend/src/app/core/model/configuration-models/login-providers.model.ts +++ b/dmp-frontend/src/app/core/model/configuration-models/login-providers.model.ts @@ -42,6 +42,11 @@ export class LoginProviders { return this._openAireConfiguration; } + private _zenodoConfiguration: LoginConfiguration; + get zenodoConfiguration(): LoginConfiguration { + return this._zenodoConfiguration; + } + public static parseValue(value: any): LoginProviders { const obj: LoginProviders = new LoginProviders(); obj._enabled = value.enabled; @@ -52,6 +57,7 @@ export class LoginProviders { obj._b2accessConfiguration = value.b2accessConfiguration; obj._orcidConfiguration = value.orcidConfiguration; obj._openAireConfiguration = value.openAireConfiguration; + obj._zenodoConfiguration = value.zenodoConfiguration; return obj; } } diff --git a/dmp-frontend/src/app/core/model/zenodo/zenodo-token.model.ts b/dmp-frontend/src/app/core/model/zenodo/zenodo-token.model.ts new file mode 100644 index 000000000..0ddb251d6 --- /dev/null +++ b/dmp-frontend/src/app/core/model/zenodo/zenodo-token.model.ts @@ -0,0 +1,6 @@ +export class ZenodoToken { + userId: string; + expiresIn: number; + accessToken:string; + email: string; +} diff --git a/dmp-frontend/src/app/ui/auth/login/img/zenodo-white-200.png b/dmp-frontend/src/app/ui/auth/login/img/zenodo-white-200.png new file mode 100644 index 000000000..5fb49be6e Binary files /dev/null and b/dmp-frontend/src/app/ui/auth/login/img/zenodo-white-200.png differ diff --git a/dmp-frontend/src/app/ui/auth/login/login.component.html b/dmp-frontend/src/app/ui/auth/login/login.component.html index d8be9c88b..dd1c8fc9e 100644 --- a/dmp-frontend/src/app/ui/auth/login/login.component.html +++ b/dmp-frontend/src/app/ui/auth/login/login.component.html @@ -54,6 +54,12 @@ +