Merge branch 'Personalized-DOI' into Development

This commit is contained in:
George Kalampokis 2020-04-09 16:49:27 +03:00
commit 36ccbb8d80
48 changed files with 913 additions and 23 deletions

View File

@ -1,6 +1,8 @@
package eu.eudat.controllers;
import eu.eudat.exceptions.security.ExpiredTokenException;
import eu.eudat.exceptions.security.NonValidTokenException;
import eu.eudat.exceptions.security.NullEmailException;
import eu.eudat.logic.managers.UserManager;
import eu.eudat.logic.proxy.config.configloaders.ConfigLoader;
@ -22,6 +24,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;
@ -37,6 +42,7 @@ import org.springframework.social.oauth1.OAuthToken;
import org.springframework.web.bind.annotation.*;
import javax.transaction.Transactional;
import java.io.IOException;
import java.security.GeneralSecurityException;
@ -54,28 +60,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 +140,12 @@ public class Login {
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<ConfigurableProviderResponseToken>().payload(this.configurableProviderTokenValidator.getAccessToken(configurableProviderRequest)).status(ApiMessageCode.NO_MESSAGE));
}
@RequestMapping(method = RequestMethod.POST, value = {"/zenodoRequestToken"}, produces = "application/json", consumes = "application/json")
public @ResponseBody
ResponseEntity<ResponseItem<ZenodoResponseToken>> ZenodoRequestToken(@RequestBody ZenodoRequest zenodoRequest) {
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<ZenodoResponseToken>().payload(this.zenodoTokenValidator.getAccessToken(zenodoRequest)).status(ApiMessageCode.NO_MESSAGE));
}
@RequestMapping(method = RequestMethod.POST, value = {"/me"}, consumes = "application/json", produces = "application/json")
public @ResponseBody
ResponseEntity<ResponseItem<Principal>> authMe(Principal principal) throws NullEmailException {

View File

@ -1,9 +1,13 @@
package eu.eudat.controllers;
import eu.eudat.data.query.items.table.userinfo.UserInfoTableRequestItem;
import eu.eudat.exceptions.security.ExpiredTokenException;
import eu.eudat.exceptions.security.NonValidTokenException;
import eu.eudat.exceptions.security.NullEmailException;
import eu.eudat.logic.managers.UserManager;
import eu.eudat.logic.security.claims.ClaimedAuthorities;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.models.data.doi.DOIRequest;
import eu.eudat.models.data.helpers.common.DataTableData;
import eu.eudat.models.data.helpers.responses.ResponseItem;
import eu.eudat.models.data.security.Principal;
@ -73,6 +77,24 @@ public class Users extends BaseController {
DataTableData<UserListingModel> dataTable = userManager.getCollaboratorsPaged(userInfoTableRequestItem, principal);
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<DataTableData<UserListingModel>>().payload(dataTable).status(ApiMessageCode.NO_MESSAGE));
}
@RequestMapping(method = RequestMethod.GET, value = {"/hasDOIToken"}, consumes = "application/json", produces = "application/json")
public @ResponseBody
ResponseEntity<ResponseItem<Boolean>> hasDOIToken(Principal principal) throws NullEmailException {
try {
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<Boolean>().payload(this.userManager.isDOITokenValid(principal)).status(ApiMessageCode.NO_MESSAGE));
} catch (NonValidTokenException | ExpiredTokenException | IOException e) {
return ResponseEntity.status(460).body(new ResponseItem<Boolean>().payload(false).status(ApiMessageCode.ERROR_MESSAGE).message(e.getMessage()));
}
}
@Transactional
@RequestMapping(method = RequestMethod.POST, value = {"/registerDOIToken"}, consumes = "application/json", produces = "application/json")
public @ResponseBody
ResponseEntity<ResponseItem<UserProfile>> registerDOIToken(@RequestBody DOIRequest doiRequest, Principal principal) throws NullEmailException, IOException {
userManager.registerDOIToken(doiRequest, principal);
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<UserProfile>().status(ApiMessageCode.NO_MESSAGE));
}
}

View File

@ -0,0 +1,8 @@
package eu.eudat.exceptions.security;
public class ExpiredTokenException extends Exception {
public ExpiredTokenException(String message) {
super(message);
}
}

View File

@ -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<Principal> {
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<Principal> {
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> {
principal.setCulture(culture);
principal.setLanguage(language);
principal.setTimezone(timezone);
principal.setZenodoToken(zenodoToken);
principal.setZenodoDuration(zenodoDuration);
return principal;
}
}

View File

@ -25,6 +25,7 @@ import eu.eudat.elastic.entities.Organization;
import eu.eudat.elastic.entities.Tag;
import eu.eudat.exceptions.datamanagementplan.DMPNewVersionException;
import eu.eudat.exceptions.datamanagementplan.DMPWithDatasetsDeleteException;
import eu.eudat.exceptions.security.NonValidTokenException;
import eu.eudat.exceptions.security.UnauthorisedException;
import eu.eudat.logic.builders.entity.UserInfoBuilder;
import eu.eudat.logic.mapper.elastic.DmpMapper;
@ -111,15 +112,17 @@ public class DataManagementPlanManager {
private DatabaseRepository databaseRepository;
private Environment environment;
private RDAManager rdaManager;
private UserManager userManager;
@Autowired
public DataManagementPlanManager(ApiContext apiContext, DatasetManager datasetManager, Environment environment, RDAManager rdaManager) {
public DataManagementPlanManager(ApiContext apiContext, DatasetManager datasetManager, Environment environment, RDAManager rdaManager, UserManager userManager) {
this.apiContext = apiContext;
this.datasetManager = datasetManager;
this.utilitiesService = apiContext.getUtilitiesService();
this.databaseRepository = apiContext.getOperationsContext().getDatabaseRepository();
this.environment = environment;
this.rdaManager = rdaManager;
this.userManager = userManager;
}
public DataTableData<DataManagementPlanListingModel> getPaged(DataManagementPlanTableRequest dataManagementPlanTableRequest, Principal principal, String fieldsGroup) throws Exception {
@ -1599,6 +1602,14 @@ public class DataManagementPlanManager {
if (dmp.getDoi() != null)
throw new Exception("DMP already has a DOI");
String zenodoToken = "";
try {
if (this.userManager.isDOITokenValid(principal)) {
zenodoToken = principal.getZenodoToken();
}
} catch (NonValidTokenException e) {
zenodoToken = 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,28 +1634,28 @@ 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<String, String>) 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<Map[]> listResponses = restTemplate.getForEntity(listUrl, Map[].class);
createResponse = listResponses.getBody()[0];
links = (LinkedHashMap<String, String>) 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<String, String>) 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<String, String>) 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");

View File

@ -7,15 +7,20 @@ import eu.eudat.data.entities.DMP;
import eu.eudat.data.entities.UserInfo;
import eu.eudat.data.entities.UserRole;
import eu.eudat.data.query.items.table.userinfo.UserInfoTableRequestItem;
import eu.eudat.exceptions.security.ExpiredTokenException;
import eu.eudat.exceptions.security.NonValidTokenException;
import eu.eudat.exceptions.security.NullEmailException;
import eu.eudat.exceptions.security.UnauthorisedException;
import eu.eudat.logic.builders.entity.UserRoleBuilder;
import eu.eudat.logic.builders.model.models.DataTableDataBuilder;
import eu.eudat.logic.security.customproviders.Zenodo.ZenodoCustomProvider;
import eu.eudat.logic.security.validators.zenodo.helpers.ZenodoResponseToken;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.logic.services.operations.authentication.AuthenticationService;
import eu.eudat.logic.utilities.builders.XmlBuilder;
import eu.eudat.models.HintedModelFactory;
import eu.eudat.models.data.dmp.DataManagementPlan;
import eu.eudat.models.data.doi.DOIRequest;
import eu.eudat.models.data.helpers.common.DataTableData;
import eu.eudat.models.data.login.Credentials;
import eu.eudat.models.data.security.Principal;
@ -24,11 +29,13 @@ import eu.eudat.models.data.userinfo.UserProfile;
import eu.eudat.queryable.QueryableList;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.io.IOException;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
@ -36,10 +43,14 @@ import java.util.stream.Collectors;
public class UserManager {
private ApiContext apiContext;
private ZenodoCustomProvider zenodoCustomProvider;
private Environment environment;
@Autowired
public UserManager(ApiContext apiContext) {
public UserManager(ApiContext apiContext, ZenodoCustomProvider zenodoCustomProvider, Environment environment) {
this.apiContext = apiContext;
this.zenodoCustomProvider = zenodoCustomProvider;
this.environment = environment;
}
public eu.eudat.models.data.user.composite.DatasetProfile generateDatasetProfileModel(eu.eudat.data.entities.DatasetProfile profile) {
@ -112,4 +123,27 @@ public class UserManager {
dataTableData.setTotalCount((long) colaborators.size());
return dataTableData;
}
public Boolean isDOITokenValid(Principal principal) throws NonValidTokenException, ExpiredTokenException, IOException {
if (principal.getZenodoToken() != null && !principal.getZenodoToken().isEmpty()) {
if (Instant.now().isBefore(principal.getZenodoDuration())) {
return true;
}
Map<String, Object> settings = Collections.singletonMap("zenodoToken", "");
this.updateSettings(settings, principal);
throw new ExpiredTokenException("Zenodo Token is expired");
}
throw new NonValidTokenException("This account has no Zenodo Token");
}
public void registerDOIToken(DOIRequest doiRequest, Principal principal) throws IOException {
ZenodoResponseToken responseToken = this.zenodoCustomProvider.getAccessToken(doiRequest.getZenodoRequest().getCode()
, this.environment.getProperty("zenodo.login.client_id")
, this.environment.getProperty("zenodo.login.client_secret")
, doiRequest.getRedirectUri());
Map<String, Object> settings = new HashMap<>();
settings.put("zenodoToken", responseToken.getAccessToken());
settings.put("expirationDate", Instant.now().plusSeconds(responseToken.getExpiresIn()).toEpochMilli());
this.updateSettings(settings, principal);
}
}

View File

@ -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);
}

View File

@ -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<String, String> 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<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
try {
Map<String, Object> values = restTemplate.postForObject(this.environment.getProperty("zenodo.login.access_token_url"), request, Map.class);
ZenodoResponseToken zenodoResponseToken = new ZenodoResponseToken();
Map<String, Object> user = (Map<String, Object>) 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);
}};
}
}

View File

@ -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;
}
}

View File

@ -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");
}

View File

@ -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"));
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<String, Object> 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<Credential> credentials = userInfo.getCredentials();

View File

@ -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<UserRole> userRoles = apiContext.getOperationsContext().getDatabaseRepository().getUserRoleDao().getUserRoles(user);

View File

@ -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<UserRole> userRoles = apiContext.getOperationsContext().getDatabaseRepository().getUserRoleDao().getUserRoles(user);

View File

@ -0,0 +1,25 @@
package eu.eudat.models.data.doi;
import eu.eudat.logic.security.validators.zenodo.helpers.ZenodoRequest;
public class DOIRequest {
private ZenodoRequest zenodoRequest;
private String redirectUri;
public ZenodoRequest getZenodoRequest() {
return zenodoRequest;
}
public void setZenodoRequest(ZenodoRequest zenodoRequest) {
this.zenodoRequest = zenodoRequest;
}
public String getRedirectUri() {
return redirectUri;
}
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
}

View File

@ -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;
}
}

View File

@ -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<Authorities> getAuthz() {
return this.authorities;

View File

@ -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=

View File

@ -2,6 +2,8 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ReloadHelperComponent } from '@app/ui/misc/reload-helper/reload-helper.component';
import { B2AccessLoginComponent } from './ui/auth/login/b2access/b2access-login.component';
import { Oauth2DialogModule } from './ui/misc/oauth2-dialog/oauth2-dialog.module';
import { Oauth2DialogComponent } from './ui/misc/oauth2-dialog/oauth2-dialog.component';
const appRoutes: Routes = [
{
@ -219,7 +221,8 @@ const appRoutes: Routes = [
data: {
},
},
{ path: 'reload', component: ReloadHelperComponent }
{ path: 'reload', component: ReloadHelperComponent },
{ path: 'oauth2', component: Oauth2DialogComponent }
];
@NgModule({

View File

@ -30,6 +30,7 @@ import { NgcCookieConsentConfig, NgcCookieConsentModule } from 'ngx-cookieconsen
import { TranslateServerLoader } from './core/services/language/server.loader';
import { BaseHttpService } from './core/services/http/base-http.service';
import { ConfigurationService } from './core/services/configuration/configuration.service';
import { Oauth2DialogModule } from './ui/misc/oauth2-dialog/oauth2-dialog.module';
// AoT requires an exported function for factories
export function HttpLoaderFactory(http: HttpClient, appConfig: ConfigurationService) {
@ -97,7 +98,8 @@ const cookieConfig: NgcCookieConsentConfig = {
DatasetCreateWizardModule,
NavbarModule,
SidebarModule,
NgcCookieConsentModule.forRoot(cookieConfig)
NgcCookieConsentModule.forRoot(cookieConfig),
Oauth2DialogModule
],
declarations: [
AppComponent,

View File

@ -7,5 +7,6 @@ export enum AuthProvider {
B2Access = 6,
ORCID = 7,
OpenAire = 8,
Configurable = 9
Configurable = 9,
Zenodo = 10
}

View File

@ -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;
}
}

View File

@ -0,0 +1,6 @@
export class ZenodoToken {
userId: string;
expiresIn: number;
accessToken:string;
email: string;
}

View File

@ -47,4 +47,14 @@ export class UserService {
getCollaboratorsPaged(dataTableRequest: DataTableRequest<UserCriteria>): Observable<DataTableData<UserListingModel>> {
return this.http.post<DataTableData<UserListingModel>>(this.actionUrl + 'getCollaboratorsPaged', JSON.stringify(dataTableRequest), { headers: this.headers });
}
public hasDOIToken(): Observable<any> {
const url = this.actionUrl + 'hasDOIToken';
return this.http.get(url, { headers: this.headers });
}
public registerDOIToken(code: string, redirectUri: string): Observable<any> {
const url = this.actionUrl + 'registerDOIToken';
return this.http.post(url, {zenodoRequest: {code: code}, redirectUri: redirectUri}, { headers: this.headers });
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -54,6 +54,12 @@
</button>
</div>
</div>
<div *ngIf="hasZenodoOauth()" class="col-auto zenodo-logo">
<button class="zenodo-button" mat-icon-button (click)="zenodoLogin()" class="login-social-button">
<span class="zenodoIcon"></span>
<!-- <span></span> -->
</button>
</div>
</div>
<div class="card-footer">
<h4 class="text-uppercase">

View File

@ -156,6 +156,12 @@
height: 90px;
}
.accesss-methods .zenodo-logo {
height: 75px;
padding-top: 8px;
margin-top: 13px;
}
.tip {
margin-top: -20px;
}
@ -231,6 +237,14 @@ span.configurableIcon {
height: 56px;
}
span.zenodoIcon {
background: url(img/zenodo-white-200.png) no-repeat 100px 56px;
background-position: center;
float: right;
width: 150px;
height: 56px;
}
.b2access-button {
margin-top: 10px;
width: fit-content;
@ -251,6 +265,11 @@ span.configurableIcon {
width: fit-content;
}
.zenodo-button {
margin-top: 10px;
width: fit-content;
}
.login-logo {
background: url(img/open-dmp.png) no-repeat;
width: 273px;

View File

@ -78,6 +78,10 @@ export class LoginComponent extends BaseComponent implements OnInit, AfterViewIn
this.router.navigate(['/login/configurable/' + provider.configurableLoginId])
}
public zenodoLogin() {
this.router.navigate(['/login/external/zenodo']);
}
public hasFacebookOauth(): boolean {
return this.hasProvider(AuthProvider.Facebook);
}
@ -106,6 +110,10 @@ export class LoginComponent extends BaseComponent implements OnInit, AfterViewIn
return this.hasProvider(AuthProvider.OpenAire);
}
public hasZenodoOauth(): boolean {
return this.hasProvider(AuthProvider.Zenodo);
}
public initProviders() {
if (this.hasProvider(AuthProvider.Google)) { this.initializeGoogleOauth(); }
if (this.hasProvider(AuthProvider.Facebook)) { this.initializeFacebookOauth(); }
@ -127,6 +135,7 @@ export class LoginComponent extends BaseComponent implements OnInit, AfterViewIn
case AuthProvider.B2Access: return this.hasAllRequiredFieldsConfigured(this.configurationService.loginProviders.b2accessConfiguration);
case AuthProvider.ORCID: return this.hasAllRequiredFieldsConfigured(this.configurationService.loginProviders.orcidConfiguration);
case AuthProvider.OpenAire: return this.hasAllRequiredFieldsConfigured(this.configurationService.loginProviders.openAireConfiguration);
case AuthProvider.Zenodo: return this.hasAllRequiredFieldsConfigured(this.configurationService.loginProviders.zenodoConfiguration);
default: throw new Error('Unsupported Provider Type');
}
}

View File

@ -12,6 +12,7 @@ import { ConfigurableProvidersService } from '@app/ui/auth/login/utilities/confi
import { LoginService } from '@app/ui/auth/login/utilities/login.service';
import { CommonFormsModule } from '@common/forms/common-forms.module';
import { CommonUiModule } from '@common/ui/common-ui.module';
import { ZenodoLoginComponent } from './zenodo-login/zenodo-login.component';
@NgModule({
imports: [
@ -27,7 +28,8 @@ import { CommonUiModule } from '@common/ui/common-ui.module';
OrcidLoginComponent,
EmailConfirmation,
OpenAireLoginComponent,
ConfigurableLoginComponent
ConfigurableLoginComponent,
ZenodoLoginComponent
],
providers: [LoginService, ConfigurableProvidersService]
})

View File

@ -8,6 +8,7 @@ import { OrcidLoginComponent } from './orcid-login/orcid-login.component';
import { TwitterLoginComponent } from './twitter-login/twitter-login.component';
import { OpenAireLoginComponent } from "./openaire-login/openaire-login.component";
import { ConfigurableLoginComponent } from "./configurable-login/configurable-login.component";
import { ZenodoLoginComponent } from './zenodo-login/zenodo-login.component';
const routes: Routes = [
{ path: '', component: LoginComponent },
@ -18,7 +19,8 @@ const routes: Routes = [
{ path: 'confirmation/:token', component: EmailConfirmation },
{ path: 'confirmation', component: EmailConfirmation },
{ path: 'openaire', component: OpenAireLoginComponent},
{ path: 'configurable/:id', component: ConfigurableLoginComponent}
{ path: 'configurable/:id', component: ConfigurableLoginComponent},
{ path: 'external/zenodo', component: ZenodoLoginComponent }
];
@NgModule({

View File

@ -0,0 +1,85 @@
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { ActivatedRoute, Params } from '@angular/router';
import { AuthProvider } from '@app/core/common/enum/auth-provider';
import { OrcidUser } from '@app/core/model/orcid/orcidUser';
import { AuthService } from '@app/core/services/auth/auth.service';
import { LoginService } from '@app/ui/auth/login/utilities/login.service';
import { BaseComponent } from '@common/base/base.component';
import { environment } from 'environments/environment';
import { takeUntil } from 'rxjs/operators';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { ZenodoToken } from '@app/core/model/zenodo/zenodo-token.model';
@Component({
selector: 'app-zenodo-login',
templateUrl: './zenodo-login.component.html',
styleUrls: ['./zenodo-login.component.scss']
})
export class ZenodoLoginComponent extends BaseComponent implements OnInit {
private returnUrl: string;
private zenodoToken: ZenodoToken
private accessToken: string;
private emailFormControl = new FormControl('');
constructor(
private route: ActivatedRoute,
private authService: AuthService,
private loginService: LoginService,
private httpClient: HttpClient,
private configurationService: ConfigurationService
) {
super();
this.zenodoToken = new ZenodoToken;
}
ngOnInit(): void {
this.route.queryParams
.pipe(takeUntil(this._destroyed))
.subscribe((params: Params) => {
const returnUrl = params['returnUrl'];
if (returnUrl) { this.returnUrl = returnUrl; }
if (!params['code']) { this.zenodoAccessGetAuthCode(); } else { this.zenodoLogin(params['code']); }
});
}
public zenodoAccessGetAuthCode() {
window.location.href = this.configurationService.loginProviders.zenodoConfiguration.oauthUrl
+ '?client_id='
+ this.configurationService.loginProviders.zenodoConfiguration.clientId
+ '&response_type=code&scope=deposit:write+deposit:actions+user:email&state=astate&redirect_uri='
+ this.configurationService.loginProviders.zenodoConfiguration.redirectUri;
}
public zenodoLogin(code: string) {
let headers = new HttpHeaders();
headers = headers.set('Content-Type', 'application/json');
headers = headers.set('Accept', 'application/json');
this.httpClient.post(this.configurationService.server + 'auth/zenodoRequestToken', { code: code }, { headers: headers })
.pipe(takeUntil(this._destroyed))
.subscribe((responseData: any) => {
this.zenodoToken.userId = responseData.payload.userId;
this.zenodoToken.expiresIn = responseData.payload.expiresIn;
this.accessToken = this.zenodoToken.accessToken = responseData.payload.accessToken;
this.zenodoToken.email = responseData.payload.email;
this.authService.login({ ticket: this.accessToken, provider: AuthProvider.Zenodo, data: this.zenodoToken })
.pipe(takeUntil(this._destroyed))
.subscribe(
res => this.loginService.onLogInSuccess(res, this.returnUrl),
error => this.loginService.onLogInError(error)
);
});
}
public login() {
this.zenodoToken.email = this.emailFormControl.value;
this.authService.login({ ticket: this.accessToken, provider: AuthProvider.Zenodo, data: this.zenodoToken })
.pipe(takeUntil(this._destroyed))
.subscribe(
res => this.loginService.onLogInSuccess(res, this.returnUrl),
error => this.loginService.onLogInError(error)
);
}
}

View File

@ -30,6 +30,7 @@ import { DmpWizardDatasetListingComponent } from '@app/ui/dmp/wizard/listing/dmp
import { CommonFormsModule } from '@common/forms/common-forms.module';
import { FormValidationErrorsDialogModule } from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.module';
import { CommonUiModule } from '@common/ui/common-ui.module';
import { MultipleChoiceDialogModule } from '@common/modules/multiple-choice-dialog/multiple-choice-dialog.module';
@NgModule({
imports: [
@ -42,7 +43,8 @@ import { CommonUiModule } from '@common/ui/common-ui.module';
AutoCompleteModule,
DmpRoutingModule,
DmpOverviewModule,
FormValidationErrorsDialogModule
FormValidationErrorsDialogModule,
MultipleChoiceDialogModule
],
declarations: [
DmpListingComponent,

View File

@ -22,6 +22,13 @@ import { Observable, of as observableOf } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Role } from "@app/core/common/enum/role";
import { DmpInvitationDialogComponent } from '../invitation/dmp-invitation.component';
import { MultipleChoiceDialogModule } from '@common/modules/multiple-choice-dialog/multiple-choice-dialog.module';
import { MultipleChoiceDialogComponent } from '@common/modules/multiple-choice-dialog/multiple-choice-dialog.component';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { Oauth2DialogComponent } from '@app/ui/misc/oauth2-dialog/oauth2-dialog.component';
import { Oauth2DialogService } from '@app/ui/misc/oauth2-dialog/service/oauth2-dialog.service';
import { isNullOrUndefined } from 'util';
import { UserService } from '@app/core/services/user/user.service';
@Component({
selector: 'app-dmp-overview',
@ -47,7 +54,10 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
private authentication: AuthService,
private dialog: MatDialog,
private language: TranslateService,
private uiNotificationService: UiNotificationService
private uiNotificationService: UiNotificationService,
private configurationService: ConfigurationService,
private oauth2DialogService: Oauth2DialogService,
private userService: UserService
) {
super();
}
@ -321,7 +331,24 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
return dmp.doi == null ? true : false;
}
getAccessUrl(): string {
const redirectUri = this.configurationService.app + 'oauth2';
const url = this.configurationService.loginProviders.zenodoConfiguration.oauthUrl
+ '?client_id=' + this.configurationService.loginProviders.zenodoConfiguration.clientId
+ '&response_type=code&scope=deposit:write+deposit:actions&state=astate&redirect_uri='
+ redirectUri;
return url;
}
getDoi(dmp: DmpOverviewModel) {
this.userService.hasDOIToken().subscribe(response => {
this.showConfirmationDOIDialog(dmp);
}, error => {
this.showErrorConfirmationDOIDialog(error.error.message, dmp);
});
}
showConfirmationDOIDialog(dmp: DmpOverviewModel) {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
maxWidth: '600px',
restoreFocus: false,
@ -346,6 +373,41 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
});
}
showErrorConfirmationDOIDialog(message: string, dmp: DmpOverviewModel) {
const dialogRef = this.dialog.open(MultipleChoiceDialogComponent, {
maxWidth: '600px',
restoreFocus: false,
data: {
message: message,
titles: [ this.language.instant('DMP-OVERVIEW.MULTIPLE-DIALOG.ZENODO-LOGIN'), this.language.instant('DMP-OVERVIEW.MULTIPLE-DIALOG.USE-DEFAULT')]
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
switch (result) {
case 0:
// this.authentication.logout();
// this.router.navigate(['/login/external/zenodo']);
this.showOauth2Dialog(this.getAccessUrl(), dmp);
break;
case 1:
this.showConfirmationDOIDialog(dmp);
break;
}
});
}
showOauth2Dialog(url: string, dmp: DmpOverviewModel) {
this.oauth2DialogService.login(url)
.pipe(takeUntil(this._destroyed))
.subscribe(code => {
if (!isNullOrUndefined(code)) {
this.userService.registerDOIToken(code, this.configurationService.app + 'oauth2')
.pipe(takeUntil(this._destroyed))
.subscribe(() => this.showConfirmationDOIDialog(dmp));
}
});
}
onDOICallbackSuccess(): void {
this.uiNotificationService.snackBarNotification(this.language.instant('DMP-EDITOR.SNACK-BAR.SUCCESSFUL-DOI'), SnackBarNotificationLevel.Success);
}

View File

@ -0,0 +1 @@
<p>oauth2-dialog works!</p>

View File

@ -0,0 +1,41 @@
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { ActivatedRoute, Params } from '@angular/router';
import { BaseComponent } from '@common/base/base.component';
import { takeUntil } from 'rxjs/operators';
import { Oauth2DialogService } from './service/oauth2-dialog.service';
@Component({
selector: 'app-oauth2-dialog',
templateUrl: './oauth2-dialog.component.html',
styleUrls: ['./oauth2-dialog.component.scss']
})
export class Oauth2DialogComponent extends BaseComponent implements OnInit{
constructor(
private route: ActivatedRoute,
private oauth2dialogService: Oauth2DialogService
) {
super();
}
ngOnInit(): void {
this.route.queryParams.pipe(takeUntil(this._destroyed))
.subscribe((params: Params) => {
const url = params['url'];
if (!params['code']) { this.loadUrl(url) } else { this.sendCode(params['code']); }
});
}
private loadUrl(url: string ) {
console.log(url);
window.location.href = url;
}
private sendCode(code: string) {
localStorage.setItem('oauthCode', code);
window.close();
}
}

View File

@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { Oauth2DialogComponent } from './oauth2-dialog.component';
import { CommonUiModule } from '@common/ui/common-ui.module';
import { Oauth2DialogService } from './service/oauth2-dialog.service';
@NgModule({
declarations: [Oauth2DialogComponent],
imports: [
CommonUiModule
],
providers: [
Oauth2DialogService
]
})
export class Oauth2DialogModule { }

View File

@ -0,0 +1,34 @@
import { Injectable, Inject } from '@angular/core';
import { BaseService } from '@common/base/base.service';
import { BehaviorSubject, Observable, interval } from 'rxjs';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { isNullOrUndefined } from 'util';
import { takeUntil } from 'rxjs/operators';
@Injectable()
export class Oauth2DialogService extends BaseService{
private code: BehaviorSubject<string> = new BehaviorSubject(undefined);
constructor(private configurationService: ConfigurationService) {
super();
}
public registerCode(code: string) {
this.code.next(code);
}
public login(url: string): Observable<string> {
const windows = window.open(this.configurationService.app + 'oauth2?url=' + encodeURIComponent(url) ,'', 'height=500px,width=500px');
const sub = interval(300).pipe(takeUntil(this._destroyed)).subscribe(() => {
if (windows.closed) {
const oauthCode = localStorage.getItem('oauthCode');
localStorage.removeItem('oauthCode');
this.code.next(oauthCode);
sub.unsubscribe();
}
});
return this.code.asObservable();
}
}

View File

@ -37,6 +37,11 @@
"oauthUrl": "",
"redirectUri": "",
"state": "987654321"
},
"zenodoConfiguration": {
"clientId": "",
"oauthUrl": "https://sandbox.zenodo.org/oauth/authorize",
"redirectUri": "http://localhost:4200/login/external/zenodo"
}
},
"logging": {

View File

@ -525,6 +525,10 @@
"ERROR": {
"DELETED-DMP": "The requested DMP is deleted",
"FORBIDEN-DMP": "You are not allowed to access this DMP"
},
"MULTIPLE-DIALOG": {
"ZENODO-LOGIN": "Login with Zenodo",
"USE-DEFAULT": "Use Default Token"
}
},
"DATASET-LISTING": {

View File

@ -0,0 +1,30 @@
<div class="confirmation-dialog">
<div *ngIf="data.icon" class="row d-flex flex-row">
<div class="col-auto close-btn justify-content-start">
<mat-icon color="warn">{{ data.icon }}</mat-icon>
</div>
<div *ngIf="data.warning" class="col justify-content-center warn-text">{{ data.warning }}</div>
<div class="col-auto close-btn justify-content-end" (click)="close()">
<mat-icon>close</mat-icon>
</div>
</div>
<div class="row d-flex flex-row">
<div class="col message pb-4 pl-3">{{ data.message }}</div>
<div *ngIf="!data.icon" class="col-auto close-btn justify-content-end" (click)="close()">
<mat-icon>close</mat-icon>
</div>
</div>
<div *ngIf="data.privacyPolicyNames" class="row d-flex flex-col">
<div class="col-12 privacy-policy-names">{{ data.privacyPolicyNames }}</div>
<mat-checkbox [(ngModel)]="agreePrivacyPolicyNames" required class="checkbox-privacy">
{{'GENERAL.CONFIRMATION-DIALOG.ACTIONS.POLICY-AGREE' | translate}}
</mat-checkbox>
<div *ngIf="!agreePrivacyPolicyNames" class="required-policy">{{'GENERAL.CONFIRMATION-DIALOG.ACTIONS.REQUIRED' | translate}}</div>
</div>
<div class="row">
<div class="col"></div>
<span *ngFor="let title of data.titles; let i = index">
<div class="col-auto"><button mat-raised-button type="button" (click)="apply(i)" class="confirm">{{ title }}</button></div>
</span>
</div>
</div>

View File

@ -0,0 +1,44 @@
.confirmation-dialog {
.confirmation {
padding-bottom: 20px;
}
.privacy-policy-names {
font-weight: 700;
padding: 1em;
}
.close-btn {
margin-left: auto;
cursor: pointer;
}
.warn-text {
color: #f44336;
}
.cancel {
background-color: #aaaaaa;
color: #ffffff;
}
.confirm {
background-color: #2cba6c;
color: #ffffff;
}
.delete {
background-color: #ba2c2c;
color: #ffffff;
}
.checkbox-privacy {
padding: 0em 1em;
}
.required-policy {
padding: 0em 1.2em 1em;
font-size: smaller;
color: #f44336;
}
}

View File

@ -0,0 +1,25 @@
import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
@Component({
selector: 'app-multiple-choice-dialog',
templateUrl: './multiple-choice-dialog.component.html',
styleUrls: ['./multiple-choice-dialog.component.scss']
})
export class MultipleChoiceDialogComponent {
agreePrivacyPolicyNames = false;
constructor(
public dialogRef: MatDialogRef<MultipleChoiceDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any
) {
}
close() {
this.dialogRef.close(false);
}
apply(i: number) {
this.dialogRef.close(i);
}
}

View File

@ -0,0 +1,15 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MultipleChoiceDialogComponent } from './multiple-choice-dialog.component';
import { CommonUiModule } from '@common/ui/common-ui.module';
import { FormsModule } from '@angular/forms';
@NgModule({
imports: [CommonUiModule, FormsModule],
declarations: [MultipleChoiceDialogComponent],
exports: [MultipleChoiceDialogComponent],
entryComponents: [MultipleChoiceDialogComponent]
})
export class MultipleChoiceDialogModule { }