From 982aea0cbfa44b8bc9c936f7c7f51eb4b717b9e2 Mon Sep 17 00:00:00 2001 From: George Kalampokis Date: Fri, 10 Apr 2020 17:16:37 +0300 Subject: [PATCH] Improved Zenodo Account Integration --- .../main/java/eu/eudat/controllers/Users.java | 8 +++ .../model/models/PrincipalBuilder.java | 14 +++++ .../eu/eudat/logic/managers/UserManager.java | 28 ++++++++-- .../Zenodo/ZenodoAccessType.java | 22 ++++++++ .../Zenodo/ZenodoCustomProvider.java | 2 +- .../Zenodo/ZenodoCustomProviderImpl.java | 7 +-- .../customproviders/Zenodo/ZenodoUser.java | 10 ++++ .../zenodo/ZenodoTokenValidator.java | 4 +- .../zenodo/helpers/ZenodoResponseToken.java | 9 ++++ .../AbstractAuthenticationService.java | 12 ++++- ...erifiedUserEmailAuthenticationService.java | 14 +++++ .../VerifiedUserAuthenticationService.java | 14 +++++ .../data/loginprovider/LoginProviderUser.java | 9 ++++ .../eudat/models/data/security/Principal.java | 18 +++++++ .../src/app/core/model/auth/principal.ts | 1 + .../src/app/core/model/user/user-listing.ts | 3 ++ .../core/model/zenodo/zenodo-token.model.ts | 1 + .../app/core/services/user/user.service.ts | 5 ++ .../zenodo-login/zenodo-login.component.ts | 1 + .../ui/dmp/overview/dmp-overview.component.ts | 41 ++++++++------ .../user-profile/user-profile.component.html | 25 ++++++++- .../user-profile/user-profile.component.scss | 4 ++ .../ui/user-profile/user-profile.component.ts | 54 +++++++++++++++++-- dmp-frontend/src/assets/i18n/en.json | 12 +++-- 24 files changed, 281 insertions(+), 37 deletions(-) create mode 100644 dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/Zenodo/ZenodoAccessType.java diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/Users.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/Users.java index 6f2205075..1cfbc4651 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/Users.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/Users.java @@ -95,6 +95,14 @@ public class Users extends BaseController { userManager.registerDOIToken(doiRequest, principal); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.NO_MESSAGE)); } + + @Transactional + @RequestMapping(method = RequestMethod.DELETE, value = {"/deleteDOIToken"}, consumes = "application/json", produces = "application/json") + public @ResponseBody + ResponseEntity> deleteDOIToken(Principal principal) throws NullEmailException, IOException { + userManager.deleteDOIToken(principal); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.NO_MESSAGE)); + } } 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 35ce34035..74ca6607b 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 @@ -25,6 +25,8 @@ public class PrincipalBuilder extends Builder { private String timezone; private String zenodoToken; private Instant zenodoDuration; + private String zenodoEmail; + private String zenodoRefresh; public PrincipalBuilder id(UUID id) { this.id = id; @@ -81,6 +83,16 @@ public class PrincipalBuilder extends Builder { return this; } + public PrincipalBuilder zenodoEmail(String zenodoEmail) { + this.zenodoEmail = zenodoEmail; + return this; + } + + public PrincipalBuilder zenodoRefresh(String zenodoRefresh) { + this.zenodoRefresh = zenodoRefresh; + return this; + } + @Override public Principal build() { Principal principal = new Principal(); @@ -95,6 +107,8 @@ public class PrincipalBuilder extends Builder { principal.setTimezone(timezone); principal.setZenodoToken(zenodoToken); principal.setZenodoDuration(zenodoDuration); + principal.setZenodoEmail(zenodoEmail); + principal.setZenodoRefresh(zenodoRefresh); return principal; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UserManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UserManager.java index 064580a7b..5573898a2 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UserManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UserManager.java @@ -13,6 +13,7 @@ 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.ZenodoAccessType; import eu.eudat.logic.security.customproviders.Zenodo.ZenodoCustomProvider; import eu.eudat.logic.security.validators.zenodo.helpers.ZenodoResponseToken; import eu.eudat.logic.services.ApiContext; @@ -129,21 +130,38 @@ public class UserManager { if (Instant.now().isBefore(principal.getZenodoDuration())) { return true; } - Map settings = Collections.singletonMap("zenodoToken", ""); - this.updateSettings(settings, principal); - throw new ExpiredTokenException("Zenodo Token is expired"); + try { + this.updateDOIToken(ZenodoAccessType.REFRESH_TOKEN, principal.getZenodoRefresh(), this.environment.getProperty("zenodo.login.redirect_uri"), principal); + }catch (IOException e) { + 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.updateDOIToken(ZenodoAccessType.AUTHORIZATION_CODE, doiRequest.getZenodoRequest().getCode(), doiRequest.getRedirectUri(), principal); + } + + private void updateDOIToken(ZenodoAccessType accessType, String code, String redirectUri, Principal principal) throws IOException { + ZenodoResponseToken responseToken = this.zenodoCustomProvider.getAccessToken(accessType, code , this.environment.getProperty("zenodo.login.client_id") , this.environment.getProperty("zenodo.login.client_secret") - , doiRequest.getRedirectUri()); + , redirectUri); Map settings = new HashMap<>(); + settings.put("zenodoEmail", responseToken.getEmail()); + settings.put("zenodoRefresh", responseToken.getRefreshToken()); settings.put("zenodoToken", responseToken.getAccessToken()); settings.put("expirationDate", Instant.now().plusSeconds(responseToken.getExpiresIn()).toEpochMilli()); this.updateSettings(settings, principal); } + + public void deleteDOIToken(Principal principal) throws IOException { + Map settings = new HashMap<>(); + settings.put("zenodoEmail", ""); + settings.put("zenodoRefresh", ""); + settings.put("zenodoToken", ""); + settings.put("expirationDate", 0); + this.updateSettings(settings, principal); + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/Zenodo/ZenodoAccessType.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/Zenodo/ZenodoAccessType.java new file mode 100644 index 000000000..79b077853 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/Zenodo/ZenodoAccessType.java @@ -0,0 +1,22 @@ +package eu.eudat.logic.security.customproviders.Zenodo; + +public enum ZenodoAccessType { + AUTHORIZATION_CODE("authorization_code", "code"), + REFRESH_TOKEN("refresh_token", "refresh_token"); + + private final String grantType; + private final String property; + + ZenodoAccessType(String grantType, String property) { + this.grantType = grantType; + this.property = property; + } + + public String getGrantType() { + return grantType; + } + + public String getProperty() { + return property; + } +} 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 index b14c3a963..9ca463034 100644 --- 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 @@ -4,5 +4,5 @@ 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); + ZenodoResponseToken getAccessToken(ZenodoAccessType accessType, 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 index b527af2fe..35572dac4 100644 --- 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 @@ -32,7 +32,7 @@ public class ZenodoCustomProviderImpl implements ZenodoCustomProvider { } @Override - public ZenodoResponseToken getAccessToken(String code, String clientId, String clientSecret, String redirectUri) { + public ZenodoResponseToken getAccessToken(ZenodoAccessType accessType, String code, String clientId, String clientSecret, String redirectUri) { RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); @@ -41,8 +41,8 @@ public class ZenodoCustomProviderImpl implements ZenodoCustomProvider { 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("grant_type", accessType.getGrantType()); + map.add(accessType.getProperty(), code); map.add("redirect_uri", redirectUri); HttpEntity> request = new HttpEntity<>(map, headers); @@ -54,6 +54,7 @@ public class ZenodoCustomProviderImpl implements ZenodoCustomProvider { zenodoResponseToken.setEmail((String) user.get("email")); zenodoResponseToken.setExpiresIn((Integer) values.get("expires_in")); zenodoResponseToken.setAccessToken((String) values.get("access_token")); + zenodoResponseToken.setRefreshToken((String) values.get("refresh_token")); return zenodoResponseToken; } catch (HttpClientErrorException ex) { 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 index cbcb95928..92480c202 100644 --- 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 @@ -7,6 +7,7 @@ public class ZenodoUser { private String email; private String accessToken; private Integer expiresIn; + private String refreshToken; public String getUserId() { return userId; @@ -39,11 +40,20 @@ public class ZenodoUser { this.expiresIn = expiresIn; } + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + 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"); + this.refreshToken = (String) ((Map) data).get("refreshToken"); return this; } } 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 index 2708a96b2..b3c7cbf3e 100644 --- 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 @@ -4,6 +4,7 @@ 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.ZenodoAccessType; import eu.eudat.logic.security.customproviders.Zenodo.ZenodoCustomProvider; import eu.eudat.logic.security.customproviders.Zenodo.ZenodoUser; import eu.eudat.logic.security.validators.TokenValidator; @@ -43,13 +44,14 @@ public class ZenodoTokenValidator implements TokenValidator { user.setEmail(zenodoUser.getEmail()); user.setZenodoId(zenodoUser.getAccessToken()); user.setZenodoExpire(zenodoUser.getExpiresIn()); + user.setZenodoRefresh(zenodoUser.getRefreshToken()); user.setProvider(credentials.getProvider()); user.setSecret(credentials.getTicket()); return this.nonVerifiedUserAuthenticationService.Touch(user); } public ZenodoResponseToken getAccessToken(ZenodoRequest zenodoRequest) { - return this.zenodoCustomProvider.getAccessToken(zenodoRequest.getCode() + return this.zenodoCustomProvider.getAccessToken(ZenodoAccessType.AUTHORIZATION_CODE, 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/ZenodoResponseToken.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/zenodo/helpers/ZenodoResponseToken.java index 5b437cdfb..6c8783acf 100644 --- 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 @@ -5,6 +5,7 @@ public class ZenodoResponseToken { private String email; private Integer expiresIn; private String accessToken; + private String refreshToken; public String getUserId() { return userId; @@ -35,4 +36,12 @@ public class ZenodoResponseToken { public void setAccessToken(String accessToken) { this.accessToken = accessToken; } + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } } 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 fc09056fa..78c6a9830 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 @@ -133,7 +133,11 @@ 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() + "\"}},{\"zenodoToken\":\"" + profile.getZenodoId() + "\", \"expirationDate\": \"" + Instant.now().plusSeconds(profile.getZenodoExpire()).toEpochMilli() + "\"}") + .additionalinfo("{\"data\":{\"avatar\":{\"url\":\"" + profile.getAvatarUrl() + + "\"}},{\"zenodoToken\":\"" + profile.getZenodoId() + + "\", \"expirationDate\": \"" + Instant.now().plusSeconds(profile.getZenodoExpire()).toEpochMilli() + + "\", \"zenodoRefresh\": \"" + profile.getZenodoRefresh() + + (profile.getProvider() == TokenValidatorFactoryImpl.LoginProvider.ZENODO ? "\", \"zenodoEmail\": \"" + profile.getEmail() : "") +"\"}") .authorization_level((short) 1).usertype((short) 1) .build(); @@ -159,6 +163,12 @@ public abstract class AbstractAuthenticationService implements AuthenticationSer if (profile.getZenodoExpire() != null) { additionalInfo.put("expirationDate", Instant.now().plusSeconds(profile.getZenodoExpire()).toEpochMilli()); } + if (profile.getZenodoRefresh() != null) { + additionalInfo.put("zenodoRefresh", profile.getZenodoRefresh()); + } + if (profile.getProvider() == TokenValidatorFactoryImpl.LoginProvider.ZENODO) { + additionalInfo.put("zenodoEmail", profile.getEmail()); + } 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 159bbb235..4d849a51c 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 @@ -46,6 +46,18 @@ public class NonVerifiedUserEmailAuthenticationService extends AbstractAuthentic } catch (Exception e) { zenodoDuration = Instant.now(); } + String zenodoEmail; + try { + zenodoEmail = user.getAdditionalinfo() != null ? new ObjectMapper().readTree(user.getAdditionalinfo()).get("zenodoEmail").asText() : ""; + } catch (Exception e) { + zenodoEmail = ""; + } + String zenodoRefresh; + try { + zenodoRefresh = user.getAdditionalinfo() != null ? new ObjectMapper().readTree(user.getAdditionalinfo()).get("zenodoRefresh").asText() : ""; + } catch (Exception e) { + zenodoRefresh = ""; + } String culture; try { culture = user.getAdditionalinfo() != null ? new ObjectMapper().readTree(user.getAdditionalinfo()).get("culture").get("name").asText() : ""; @@ -73,6 +85,8 @@ public class NonVerifiedUserEmailAuthenticationService extends AbstractAuthentic .timezone(timezone) .zenodoToken(zenodoToken) .zenodoDuration(zenodoDuration) + .zenodoEmail(zenodoEmail) + .zenodoRefresh(zenodoRefresh) .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 c03e81934..d613b2b47 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 @@ -55,6 +55,18 @@ public class VerifiedUserAuthenticationService extends AbstractAuthenticationSer } catch (Exception e) { zenodoDuration = Instant.now(); } + String zenodoEmail; + try { + zenodoEmail = user.getAdditionalinfo() != null ? new ObjectMapper().readTree(user.getAdditionalinfo()).get("zenodoEmail").asText() : ""; + } catch (Exception e) { + zenodoEmail = ""; + } + String zenodoRefresh; + try { + zenodoRefresh = user.getAdditionalinfo() != null ? new ObjectMapper().readTree(user.getAdditionalinfo()).get("zenodoRefresh").asText() : ""; + } catch (Exception e) { + zenodoRefresh = ""; + } String culture; try { culture = user.getAdditionalinfo() != null ? new ObjectMapper().readTree(user.getAdditionalinfo()).get("culture").get("name").asText() : ""; @@ -82,6 +94,8 @@ public class VerifiedUserAuthenticationService extends AbstractAuthenticationSer .timezone(timezone) .zenodoToken(zenodoToken) .zenodoDuration(zenodoDuration) + .zenodoEmail(zenodoEmail) + .zenodoRefresh(zenodoRefresh) .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 13f5b9371..6d1d9b02e 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 @@ -13,6 +13,7 @@ public class LoginProviderUser { private TokenValidatorFactoryImpl.LoginProvider provider; private String zenodoId; private Integer zenodoExpire; + private String zenodoRefresh; public String getId() { return id; @@ -85,4 +86,12 @@ public class LoginProviderUser { public void setZenodoExpire(Integer zenodoExpire) { this.zenodoExpire = zenodoExpire; } + + public String getZenodoRefresh() { + return zenodoRefresh; + } + + public void setZenodoRefresh(String zenodoRefresh) { + this.zenodoRefresh = zenodoRefresh; + } } 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 f0e017e84..7f2992194 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 @@ -20,6 +20,8 @@ public class Principal { private String timezone; private String zenodoToken; private Instant zenodoDuration; + private String zenodoRefresh; + private String zenodoEmail; public UUID getId() { return id; @@ -105,6 +107,22 @@ public class Principal { this.zenodoDuration = zenodoDuration; } + public String getZenodoRefresh() { + return zenodoRefresh; + } + + public void setZenodoRefresh(String zenodoRefresh) { + this.zenodoRefresh = zenodoRefresh; + } + + public String getZenodoEmail() { + return zenodoEmail; + } + + public void setZenodoEmail(String zenodoEmail) { + this.zenodoEmail = zenodoEmail; + } + @JsonIgnore public Set getAuthz() { return this.authorities; diff --git a/dmp-frontend/src/app/core/model/auth/principal.ts b/dmp-frontend/src/app/core/model/auth/principal.ts index 15937a98c..e5131a436 100644 --- a/dmp-frontend/src/app/core/model/auth/principal.ts +++ b/dmp-frontend/src/app/core/model/auth/principal.ts @@ -10,4 +10,5 @@ export interface Principal { timezone: string; language: string; culture: string; + zenodoEmail: string; } diff --git a/dmp-frontend/src/app/core/model/user/user-listing.ts b/dmp-frontend/src/app/core/model/user/user-listing.ts index 2ba60a45d..1abc5383b 100644 --- a/dmp-frontend/src/app/core/model/user/user-listing.ts +++ b/dmp-frontend/src/app/core/model/user/user-listing.ts @@ -3,5 +3,8 @@ import { AppRole } from "../../common/enum/app-role"; export interface UserListingModel { id: String; name: String; + email: string; appRoles: AppRole[]; + additionalinfo: any; + associatedDmps: any[]; } 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 index 0ddb251d6..a1428496f 100644 --- a/dmp-frontend/src/app/core/model/zenodo/zenodo-token.model.ts +++ b/dmp-frontend/src/app/core/model/zenodo/zenodo-token.model.ts @@ -3,4 +3,5 @@ export class ZenodoToken { expiresIn: number; accessToken:string; email: string; + refreshToken: string; } diff --git a/dmp-frontend/src/app/core/services/user/user.service.ts b/dmp-frontend/src/app/core/services/user/user.service.ts index d3ea3f9cc..27165ca96 100644 --- a/dmp-frontend/src/app/core/services/user/user.service.ts +++ b/dmp-frontend/src/app/core/services/user/user.service.ts @@ -57,4 +57,9 @@ export class UserService { const url = this.actionUrl + 'registerDOIToken'; return this.http.post(url, {zenodoRequest: {code: code}, redirectUri: redirectUri}, { headers: this.headers }); } + + public deleteDOIToken(): Observable { + const url = this.actionUrl + 'deleteDOIToken'; + return this.http.delete(url, { headers: this.headers }); + } } diff --git a/dmp-frontend/src/app/ui/auth/login/zenodo-login/zenodo-login.component.ts b/dmp-frontend/src/app/ui/auth/login/zenodo-login/zenodo-login.component.ts index 36decce42..b58a77974 100644 --- a/dmp-frontend/src/app/ui/auth/login/zenodo-login/zenodo-login.component.ts +++ b/dmp-frontend/src/app/ui/auth/login/zenodo-login/zenodo-login.component.ts @@ -64,6 +64,7 @@ export class ZenodoLoginComponent extends BaseComponent implements OnInit { this.zenodoToken.expiresIn = responseData.payload.expiresIn; this.accessToken = this.zenodoToken.accessToken = responseData.payload.accessToken; this.zenodoToken.email = responseData.payload.email; + this.zenodoToken.refreshToken = responseData.payload.refreshToken; this.authService.login({ ticket: this.accessToken, provider: AuthProvider.Zenodo, data: this.zenodoToken }) .pipe(takeUntil(this._destroyed)) .subscribe( diff --git a/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts b/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts index 625f5994a..1bbe67bae 100644 --- a/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts +++ b/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts @@ -45,6 +45,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { breadCrumbs: Observable = observableOf(); isUserOwner: boolean; expand = false; + hasDOIToken = false; constructor( private route: ActivatedRoute, @@ -149,14 +150,14 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { this.expand = !this.expand; } - checkOverflow (element) { + checkOverflow(element) { if (this.expand || (element.offsetHeight < element.scrollHeight || element.offsetWidth < element.scrollWidth)) { - return true; + return true; } else { - return false; + return false; } - } + } datasetClicked(datasetId: String) { if (this.isPublicView) { @@ -334,26 +335,29 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { 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; + + '?client_id=' + this.configurationService.loginProviders.zenodoConfiguration.clientId + + '&response_type=code&scope=deposit:write+deposit:actions+user:email&state=astate&redirect_uri=' + + redirectUri; return url; } getDoi(dmp: DmpOverviewModel) { this.userService.hasDOIToken().subscribe(response => { + this.hasDOIToken = true; this.showConfirmationDOIDialog(dmp); }, error => { + this.hasDOIToken = false; this.showErrorConfirmationDOIDialog(error.error.message, dmp); }); } + showConfirmationDOIDialog(dmp: DmpOverviewModel) { const dialogRef = this.dialog.open(ConfirmationDialogComponent, { maxWidth: '600px', restoreFocus: false, data: { - message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ZENODO-DOI'), + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ZENODO-DOI', { 'username': this.hasDOIToken ? this.authentication.current().zenodoEmail : 'default'}), confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'), } @@ -379,7 +383,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { restoreFocus: false, data: { message: message, - titles: [ this.language.instant('DMP-OVERVIEW.MULTIPLE-DIALOG.ZENODO-LOGIN'), this.language.instant('DMP-OVERVIEW.MULTIPLE-DIALOG.USE-DEFAULT')] + 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 => { @@ -398,14 +402,17 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { 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)); - } - }); + .pipe(takeUntil(this._destroyed)) + .subscribe(code => { + if (!isNullOrUndefined(code)) { + this.userService.registerDOIToken(code, this.configurationService.app + 'oauth2') + .pipe(takeUntil(this._destroyed)) + .subscribe(() => { + this.hasDOIToken = true; + this.showConfirmationDOIDialog(dmp); + }); + } + }); } onDOICallbackSuccess(): void { diff --git a/dmp-frontend/src/app/ui/user-profile/user-profile.component.html b/dmp-frontend/src/app/ui/user-profile/user-profile.component.html index f3661a928..3e8c2eddf 100644 --- a/dmp-frontend/src/app/ui/user-profile/user-profile.component.html +++ b/dmp-frontend/src/app/ui/user-profile/user-profile.component.html @@ -63,7 +63,7 @@ - +
@@ -98,6 +98,29 @@
+ + + +
+

{{ 'USER-PROFILE.ZENODO.TITLE' | translate}}

+
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+
+
diff --git a/dmp-frontend/src/app/ui/user-profile/user-profile.component.scss b/dmp-frontend/src/app/ui/user-profile/user-profile.component.scss index 36426b892..fe59534bc 100644 --- a/dmp-frontend/src/app/ui/user-profile/user-profile.component.scss +++ b/dmp-frontend/src/app/ui/user-profile/user-profile.component.scss @@ -25,3 +25,7 @@ .settings { line-height: 2.4em !important; } + +.zenodo-email { + color: black; +} diff --git a/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts b/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts index dcef445ee..2f8b08b95 100644 --- a/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts +++ b/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts @@ -13,6 +13,9 @@ import * as moment from 'moment-timezone'; import { Observable, of } from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; import { LanguageService } from '@app/core/services/language/language.service'; +import { isNullOrUndefined } from 'util'; +import { Oauth2DialogService } from '../misc/oauth2-dialog/service/oauth2-dialog.service'; +import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; const availableLanguages: any[] = require('../../../assets/resources/language.json'); @@ -29,6 +32,8 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes timezones: Observable; editMode = false; languages = availableLanguages; + zenodoToken: string; + zenodoEmail: string; formGroup: FormGroup; constructor( @@ -39,7 +44,9 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes private language: TranslateService, private cultureService: CultureService, private authentication: AuthService, - private languageService: LanguageService + private languageService: LanguageService, + private configurationService: ConfigurationService, + private oauth2DialogService: Oauth2DialogService ) { super(); } ngOnInit() { @@ -50,6 +57,8 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes const userId = !params['id'] ? 'me' : params['id']; this.user = this.userService.getUser(userId).pipe(map(result => { result['additionalinfo'] = JSON.parse(result['additionalinfo']); + this.zenodoToken = result['additionalinfo']['zenodoToken']; + this.zenodoEmail = result['additionalinfo']['zenodoEmail']; this.formGroup = new FormBuilder().group({ language: new FormControl(result['additionalinfo']['language'] ? availableLanguages.filter(x => x.value === result['additionalinfo']['language']['value']).pop() : '', [Validators.required]), timezone: new FormControl(result['additionalinfo']['timezone'], [Validators.required]), @@ -78,10 +87,10 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes getUserRole(dmp: DmpModel) { if (dmp.creator.id === this.currentUserId) { - return this.language.instant('USER-PROFILE.DMPS.CREATOR'); - } else if (dmp.associatedUsers.map(x => x.id).indexOf(this.currentUserId) !== -1) { - return this.language.instant('USER-PROFILE.DMPS.MEMBER'); - } + return this.language.instant('USER-PROFILE.DMPS.CREATOR'); + } else if (dmp.associatedUsers.map(x => x.id).indexOf(this.currentUserId) !== -1) { + return this.language.instant('USER-PROFILE.DMPS.MEMBER'); + } } showAllDmps() { @@ -147,4 +156,39 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes (ev.target as HTMLImageElement).src = 'assets/images/profile-placeholder.png'; } + public hasZenodo(): boolean { + return !isNullOrUndefined(this.zenodoToken) && this.zenodoToken !== ""; + } + + public loginToZenodo() { + this.showOauth2Dialog(this.getAccessUrl()); + } + + 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+user:email&state=astate&redirect_uri=' + + redirectUri; + return url; + } + + showOauth2Dialog(url: string) { + 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.router.navigate(['/reload']).then(() => this.router.navigate(['/profile']))); + } + }); + } + + public RemoveZenodo() { + this.userService.deleteDOIToken() + .pipe(takeUntil(this._destroyed)) + .subscribe(() => this.router.navigate(['/reload']).then(() => this.router.navigate(['/profile']))); + } + } diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index 8d01e4426..d11e07c5b 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -52,7 +52,7 @@ "FINALIZE-ITEM": "Finalize this item?", "PUBLISH-ITEM": "Publish this item?", "ADD-DATASET": "Do you want to continue by adding a Dataset Description to your DMP? You can always add more Dataset Descriptions using \"Add Dataset Description (Wizard)\" menu.", - "ZENODO-DOI": "Would you like to create digital object identifier (DOI) for the DMP?", + "ZENODO-DOI": "Would you like to create digital object identifier (DOI) with {{username}} account for the DMP?", "LEAVE-PAGE": "If you leave, your changes will be lost.", "LEAVE-WARNING": "You have unsaved changes!", "PRIVACY-POLICY-NAMES": "The names of all people involved into this DMP will be publicly visible. Do you agree with that?", @@ -218,7 +218,7 @@ "DATASET-TEMPLATES": "Dataset Description Templates", "DMP-TEMPLATES": "DMP Templates", "USERS": "Users", - "LANGUAGE-EDITOR":"Language Editor", + "LANGUAGE-EDITOR": "Language Editor", "GUIDE-EDITOR": "User Guide Editor" }, "DATASET-PROFILE-EDITOR": { @@ -431,7 +431,7 @@ "TITLE": "Published Data Management Plans", "OWNER": "Owner", "MEMBER": "Member", - "CREATOR":"Creator", + "CREATOR": "Creator", "VIEW-ONLY": "View Only", "TOOLTIP": { "PUBLISHED": "Public access - Closed DMP", @@ -1090,6 +1090,12 @@ "SHOW-ALL": "Show All", "CREATOR": "Creator", "MEMBER": "Member" + }, + "ZENODO": { + "LOGIN": "Login to Zenodo", + "LOGOUT": "Remove Zenodo", + "TITLE": "Zenodo Account", + "DESCRIPTION": "Linked Zenodo Account:" } }, "DATASET-REFERENCED-MODELS": {