From a02668003ddf32c1b16f3e5a213396e6bc9bbfe4 Mon Sep 17 00:00:00 2001
From: Filipe Dias Lewandowski
Date: Wed, 17 Apr 2024 16:03:55 +0200
Subject: [PATCH 1/3] Closes #2425: Adding ORCID and affiliation ROR
identifiers to the user profile (#2449)
* Closes #2425: Adding ORCID and affiliation ROR identifiers to the user profile
* Deduplicate validation/suggestion code
* review comments
---
.../persistence/user/AuthenticatedUser.java | 26 ++++++-
.../user/AuthenticatedUserDisplayInfo.java | 34 ++++++--
.../user/RoleAssigneeDisplayInfo.java | 22 ++++--
.../src/main/resources/Bundle_en.properties | 10 +++
.../src/main/resources/Bundle_pl.properties | 10 +++
.../db/migration/V72__addUserORCIDAndROR.sql | 1 +
.../user/AuthenticatedUserTest.java | 12 ++-
dataverse-webapp/pom.xml | 12 +++
.../authorization/EditableAccountField.java | 4 +-
.../EditableAccountFieldSets.java | 3 +-
.../providers/builtin/DataverseUserPage.java | 3 +-
.../providers/common/BaseUserPage.java | 64 +++++++++++++++
.../common/ExternalIdpFirstLoginPage.java | 6 +-
.../dataverse/validation/OrcidValidator.java | 49 ++++++++++++
.../{field/validators => }/RorValidator.java | 30 +++-----
.../validation/field/ValidationResult.java | 27 +++++--
.../field/validators/RorFieldValidator.java | 39 ++++++++++
.../src/main/webapp/dataverseuser.xhtml | 77 +++++++++++++++++++
.../src/main/webapp/firstLogin.xhtml | 48 ++++++++++++
.../AuthenticatedUserDisplayInfoTest.java | 9 ++-
.../mocks/MockAuthenticatedUser.java | 4 +-
.../validation/OrcidValidatorTest.java | 32 ++++++++
.../validation/RorValidatorTest.java | 39 ++++++++++
.../validators/RorFieldValidatorTest.java | 65 ++++++++++++++++
.../field/validators/RorValidatorTest.java | 47 -----------
25 files changed, 575 insertions(+), 98 deletions(-)
create mode 100644 dataverse-persistence/src/main/resources/db/migration/V72__addUserORCIDAndROR.sql
create mode 100644 dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/providers/common/BaseUserPage.java
create mode 100644 dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/OrcidValidator.java
rename dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/{field/validators => }/RorValidator.java (62%)
create mode 100644 dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/field/validators/RorFieldValidator.java
create mode 100644 dataverse-webapp/src/test/java/edu/harvard/iq/dataverse/validation/OrcidValidatorTest.java
create mode 100644 dataverse-webapp/src/test/java/edu/harvard/iq/dataverse/validation/RorValidatorTest.java
create mode 100644 dataverse-webapp/src/test/java/edu/harvard/iq/dataverse/validation/field/validators/RorFieldValidatorTest.java
delete mode 100644 dataverse-webapp/src/test/java/edu/harvard/iq/dataverse/validation/field/validators/RorValidatorTest.java
diff --git a/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUser.java b/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUser.java
index cbc82dbef0..94a7e63f70 100644
--- a/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUser.java
+++ b/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUser.java
@@ -83,7 +83,9 @@ public class AuthenticatedUser implements User, Serializable, JpaEntity {
@NotNull
@Column(nullable = false, unique = true)
private String email;
+ private String orcid;
private String affiliation;
+ private String affiliationROR;
private String position;
@NotBlank(message = "{user.lastName}")
@@ -148,10 +150,18 @@ public String getEmail() {
return email;
}
+ public String getOrcid() {
+ return orcid;
+ }
+
public String getAffiliation() {
return affiliation;
}
+ public String getAffiliationROR() {
+ return affiliationROR;
+ }
+
public String getPosition() {
return position;
}
@@ -214,7 +224,7 @@ public String getIdentifier() {
@Override
public AuthenticatedUserDisplayInfo getDisplayInfo() {
- return new AuthenticatedUserDisplayInfo(firstName, lastName, email, affiliation, position);
+ return new AuthenticatedUserDisplayInfo(firstName, lastName, email, orcid, affiliation, affiliationROR, position);
}
/**
@@ -233,6 +243,12 @@ public void applyDisplayInfo(AuthenticatedUserDisplayInfo inf) {
if (StringUtils.isNotBlank(inf.getPosition())) {
setPosition(inf.getPosition());
}
+ if (StringUtils.isNotBlank(inf.getOrcid())) {
+ setOrcid(inf.getOrcid());
+ }
+ if (StringUtils.isNotBlank(inf.getAffiliationROR())) {
+ setAffiliationROR(inf.getAffiliationROR());
+ }
}
@Override
@@ -277,10 +293,18 @@ public void setEmail(String email) {
this.email = email.trim();
}
+ public void setOrcid(String orcid) {
+ this.orcid = orcid;
+ }
+
public void setAffiliation(String affiliation) {
this.affiliation = affiliation;
}
+ public void setAffiliationROR(String affiliationROR) {
+ this.affiliationROR = affiliationROR;
+ }
+
public void setPosition(String position) {
this.position = position;
}
diff --git a/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUserDisplayInfo.java b/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUserDisplayInfo.java
index 68cdf59432..8f4b65edef 100644
--- a/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUserDisplayInfo.java
+++ b/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUserDisplayInfo.java
@@ -14,23 +14,35 @@ public class AuthenticatedUserDisplayInfo extends RoleAssigneeDisplayInfo {
@NotBlank(message = "{user.firstName}")
private String firstName;
private String position;
+ private String orcid;
/*
* @todo Shouldn't we persist the displayName too? It still exists on the
* authenticateduser table.
*/
- public AuthenticatedUserDisplayInfo(String firstName, String lastName, String emailAddress, String affiliation, String position) {
- super(firstName + " " + lastName, emailAddress, affiliation);
+ public AuthenticatedUserDisplayInfo(String firstName, String lastName, String emailAddress,
+ String affiliation, String position) {
+ super(firstName + " " + lastName, emailAddress, affiliation, null);
this.firstName = firstName;
this.lastName = lastName;
this.position = position;
}
+ public AuthenticatedUserDisplayInfo(String firstName, String lastName, String emailAddress, String orcid,
+ String affiliation, String affiliationROR, String position) {
+ super(firstName + " " + lastName, emailAddress, affiliation, affiliationROR);
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.position = position;
+ this.orcid = orcid;
+ }
+
public AuthenticatedUserDisplayInfo() {
- super("", "", "");
+ super("", "", "", "");
firstName = "";
lastName = "";
position = "";
+ orcid = "";
}
@@ -40,7 +52,8 @@ public AuthenticatedUserDisplayInfo() {
* @param src the display info {@code this} will be a copy of.
*/
public AuthenticatedUserDisplayInfo(AuthenticatedUserDisplayInfo src) {
- this(src.getFirstName(), src.getLastName(), src.getEmailAddress(), src.getAffiliation(), src.getPosition());
+ this(src.getFirstName(), src.getLastName(), src.getEmailAddress(), src.getOrcid(), src.getAffiliation(),
+ src.getAffiliationROR(), src.getPosition());
}
public String getLastName() {
@@ -67,9 +80,18 @@ public void setPosition(String position) {
this.position = position;
}
+ public String getOrcid() {
+ return orcid;
+ }
+
+ public void setOrcid(String orcid) {
+ this.orcid = orcid;
+ }
+
@Override
public String toString() {
- return "AuthenticatedUserDisplayInfo{firstName=" + firstName + ", lastName=" + lastName + ", position=" + position + ", email=" + getEmailAddress() + '}';
+ return "AuthenticatedUserDisplayInfo{firstName=" + firstName + ", lastName=" + lastName +
+ ", position=" + position + ", email=" + getEmailAddress() + ", orcid=" + getOrcid() + '}';
}
@Override
@@ -97,7 +119,7 @@ public boolean equals(Object obj) {
if (!Objects.equals(this.firstName, other.firstName)) {
return false;
}
- return Objects.equals(this.position, other.position) && super.equals(obj);
+ return Objects.equals(this.position, other.position) && Objects.equals(this.orcid, other.orcid) && super.equals(obj);
}
}
diff --git a/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/RoleAssigneeDisplayInfo.java b/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/RoleAssigneeDisplayInfo.java
index 8975b57796..4177fb195b 100644
--- a/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/RoleAssigneeDisplayInfo.java
+++ b/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/RoleAssigneeDisplayInfo.java
@@ -13,15 +13,17 @@ public class RoleAssigneeDisplayInfo implements java.io.Serializable {
private String title;
private String emailAddress;
private String affiliation;
+ private String affiliationROR;
public RoleAssigneeDisplayInfo(String title, String emailAddress) {
- this(title, emailAddress, null);
+ this(title, emailAddress, null, null);
}
- public RoleAssigneeDisplayInfo(String title, String emailAddress, String anAffiliation) {
+ public RoleAssigneeDisplayInfo(String title, String emailAddress, String anAffiliation, String affiliationROR) {
this.title = title;
this.emailAddress = emailAddress;
- affiliation = anAffiliation;
+ this.affiliation = anAffiliation;
+ this.affiliationROR = affiliationROR;
}
public String getTitle() {
@@ -36,6 +38,10 @@ public String getAffiliation() {
return affiliation;
}
+ public String getAffiliationROR() {
+ return affiliationROR;
+ }
+
public void setTitle(String title) {
this.title = title;
}
@@ -48,9 +54,14 @@ public void setAffiliation(String affiliation) {
this.affiliation = affiliation;
}
+ public void setAffiliationROR(String affiliationROR) {
+ this.affiliationROR = affiliationROR;
+ }
+
@Override
public String toString() {
- return "RoleAssigneeDisplayInfo{" + "title=" + title + ", emailAddress=" + emailAddress + ", affiliation=" + affiliation + '}';
+ return "RoleAssigneeDisplayInfo{" + "title=" + title + ", emailAddress=" + emailAddress +
+ ", affiliation=" + affiliation + ", affiliationROR=" + affiliationROR + '}';
}
@Override
@@ -78,7 +89,8 @@ public boolean equals(Object obj) {
if (!Objects.equals(this.emailAddress, other.emailAddress)) {
return false;
}
- return Objects.equals(this.affiliation, other.affiliation);
+ return Objects.equals(this.affiliation, other.affiliation)
+ && Objects.equals(this.affiliationROR, other.affiliationROR);
}
}
diff --git a/dataverse-persistence/src/main/resources/Bundle_en.properties b/dataverse-persistence/src/main/resources/Bundle_en.properties
index f095fb13ab..98ca5c4b87 100755
--- a/dataverse-persistence/src/main/resources/Bundle_en.properties
+++ b/dataverse-persistence/src/main/resources/Bundle_en.properties
@@ -331,7 +331,17 @@ user.lastName=Family Name
user.lastName.tip=The last name you would like to use for this account.
user.email.tip=A valid email address you have access to in order to be contacted.
user.email.taken=This email address is already taken.
+user.orcid=ORCID
+user.orcid.tip=ORCID is an open digital identifier allowing distinction between researchers having the same or similar name and surname.
+user.orcid.invalid.format=Invalid ORCID format.
+user.orcid.invalid.checksum=Invalid ORCID checksum.
user.affiliation.tip=The organization with which you are affiliated.
+user.affiliationror=Affiliation ROR
+user.affiliationror.tip=ROR identifier of the affiliating institution. The identifier must be provided in its URL form, for instance "https://ror.org/039bjqg32" for the University of Warsaw. You can also enter the name of the institution and select its ROR ID from the list.
+user.affiliationror.suggestionDisplay.valueHeader=ROR
+user.affiliationror.suggestionDisplay.detailsHeader=Institution
+user.affiliationror.ror.invalid.format=Invalid ROR format.
+user.affiliationror.ror.invalid.checksum=Invalid ROR checksum.
user.position=Position
user.position.tip=Your role or title at the organization you are affiliated with; such as staff, faculty, student, etc.
user.notificationsLanguage=E-mail notifications language
diff --git a/dataverse-persistence/src/main/resources/Bundle_pl.properties b/dataverse-persistence/src/main/resources/Bundle_pl.properties
index 7d9482f13c..de0a92eb4f 100644
--- a/dataverse-persistence/src/main/resources/Bundle_pl.properties
+++ b/dataverse-persistence/src/main/resources/Bundle_pl.properties
@@ -331,7 +331,17 @@ user.lastName=Nazwisko
user.lastName.tip=Nazwisko, kt\u00F3re chcesz u\u017Cy\u0107 dla tego konta.
user.email.tip=Aktualny adres e-mail, do kt\u00F3rego posiadasz dost\u0119p i kt\u00F3rego mo\u017Cna u\u017Cy\u0107, by si\u0119 z Tob\u0105 skontaktowa\u0107.
user.email.taken=Ten adres e-mail jest ju\u017C przypisany do innego konta.
+user.orcid=ORCID
+user.orcid.tip=ORCID to otwarty identyfikator cyfrowy umo\u017Cliwiaj\u0105cy rozr\u00F3\u017Cnienie naukowc\u00F3w o tym samym lub podobnym imieniu i nazwisku.
+user.orcid.invalid.format=Nieprawid\u0142owo zbudowany identyfikator.
+user.orcid.invalid.checksum=Nieprawid\u0142owy identyfikator.
user.affiliation.tip=Instytucja przy kt\u00F3rej jeste\u015B afiliowana/afiliowany.
+user.affiliationror=ROR instytucji afiliuj\u0105cej
+user.affiliationror.tip=Identyfikator ROR instytucji afiliuj\u0105cej. Identyfikator musi zosta\u0107 podany w formie adresu URL, np. "https://ror.org/039bjqg32" dla Uniwersytetu Warszawskiego. W to pole mo\u017Cesz r\u00F3wnie\u017C wprowadzi\u0107 nazw\u0119 instytucji i wybra\u0107 jej identyfikator ROR z listy.
+user.affiliationror.suggestionDisplay.valueHeader=ROR
+user.affiliationror.suggestionDisplay.detailsHeader=Instytucja
+user.affiliationror.ror.invalid.format=Nieprawid\u0142owo zbudowany identyfikator ROR.
+user.affiliationror.ror.invalid.checksum=Nieprawid\u0142owy identyfikator ROR.
user.position=Stanowisko
user.position.tip=Stanowisko lub rola jak\u0105 pe\u0142nisz w instytucji, przy kt\u00F3rej jeste\u015B afiliowana/afiliowany, na przyk\u0142ad "pracownik naukowy", "student", "pracownik administracyjny" itp.
user.notificationsLanguage=J\u0119zyk powiadomie\u0144 e-mailowych
diff --git a/dataverse-persistence/src/main/resources/db/migration/V72__addUserORCIDAndROR.sql b/dataverse-persistence/src/main/resources/db/migration/V72__addUserORCIDAndROR.sql
new file mode 100644
index 0000000000..4822df480e
--- /dev/null
+++ b/dataverse-persistence/src/main/resources/db/migration/V72__addUserORCIDAndROR.sql
@@ -0,0 +1 @@
+ALTER TABLE authenticateduser ADD COLUMN orcid TEXT, ADD COLUMN affiliationror TEXT;
diff --git a/dataverse-persistence/src/test/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUserTest.java b/dataverse-persistence/src/test/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUserTest.java
index 87f45622d8..0ef2bd15d1 100644
--- a/dataverse-persistence/src/test/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUserTest.java
+++ b/dataverse-persistence/src/test/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUserTest.java
@@ -45,7 +45,8 @@ public void testGetIdentifier() {
@Test
public void testApplyDisplayInfo() {
System.out.println("applyDisplayInfo");
- AuthenticatedUserDisplayInfo inf = new AuthenticatedUserDisplayInfo("Homer", "Simpson", "Homer.Simpson@someU.edu", "UnitTester", "In-Memory user");
+ AuthenticatedUserDisplayInfo inf = new AuthenticatedUserDisplayInfo("Homer", "Simpson", "Homer.Simpson@someU.edu",
+ "0000-0001-2345-6789", "UnitTester", "https://ror.org/04k0tth05", "In-Memory user");
testUser.applyDisplayInfo(inf);
assertEquals(inf, testUser.getDisplayInfo());
}
@@ -53,8 +54,15 @@ public void testApplyDisplayInfo() {
@Test
public void testGetDisplayInfo() {
System.out.println("getDisplayInfo");
- AuthenticatedUserDisplayInfo expResult = new AuthenticatedUserDisplayInfo("Homer", "Simpson", "Homer.Simpson@someU.edu", "UnitTester", "In-Memory user");
+ // given
+ testUser.setOrcid("0000-0001-2345-6789");
+ testUser.setAffiliationROR("https://ror.org/04k0tth05");
+ AuthenticatedUserDisplayInfo expResult = new AuthenticatedUserDisplayInfo("Homer", "Simpson", "Homer.Simpson@someU.edu",
+ "0000-0001-2345-6789", "UnitTester", "https://ror.org/04k0tth05", "In-Memory user");
+ // when
AuthenticatedUserDisplayInfo result = testUser.getDisplayInfo();
+
+ // then
assertEquals(expResult, result);
}
diff --git a/dataverse-webapp/pom.xml b/dataverse-webapp/pom.xml
index 362fed94ec..43dd125548 100644
--- a/dataverse-webapp/pom.xml
+++ b/dataverse-webapp/pom.xml
@@ -45,6 +45,12 @@
org.apache.httpcomponents
fluent-hc
+
+
+ commons-logging
+ commons-logging
+
+
org.apache.httpcomponents
@@ -431,6 +437,12 @@
com.onelogin
java-saml
2.8.0
+
+
+ org.apache.santuario
+ xmlsec
+
+
diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/EditableAccountField.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/EditableAccountField.java
index 336df4eb9b..44fbb8e642 100644
--- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/EditableAccountField.java
+++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/EditableAccountField.java
@@ -9,6 +9,8 @@ public enum EditableAccountField {
FAMILY_NAME,
EMAIL,
AFFILIATION,
+ AFFILIATION_ROR,
POSITION,
- NOTIFICATIONS_LANG
+ NOTIFICATIONS_LANG,
+ ORCID
}
diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/EditableAccountFieldSets.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/EditableAccountFieldSets.java
index 930b2e8bd9..780659d69f 100644
--- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/EditableAccountFieldSets.java
+++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/EditableAccountFieldSets.java
@@ -33,7 +33,8 @@ public static Set createAllFieldsSet() {
public static Set createSecondaryFieldsSet() {
return Collections.unmodifiableSet(
- Stream.of(EditableAccountField.AFFILIATION, EditableAccountField.POSITION, EditableAccountField.NOTIFICATIONS_LANG)
+ Stream.of(EditableAccountField.AFFILIATION, EditableAccountField.POSITION, EditableAccountField.NOTIFICATIONS_LANG,
+ EditableAccountField.ORCID, EditableAccountField.AFFILIATION_ROR)
.collect(Collectors.toSet()));
}
diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/providers/builtin/DataverseUserPage.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/providers/builtin/DataverseUserPage.java
index f90f39aaf0..546081781c 100644
--- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/providers/builtin/DataverseUserPage.java
+++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/providers/builtin/DataverseUserPage.java
@@ -8,6 +8,7 @@
import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean;
import edu.harvard.iq.dataverse.authorization.EditableAccountField;
import edu.harvard.iq.dataverse.authorization.UserRecordIdentifier;
+import edu.harvard.iq.dataverse.authorization.providers.common.BaseUserPage;
import edu.harvard.iq.dataverse.authorization.providers.shib.ShibAuthenticationProvider;
import edu.harvard.iq.dataverse.common.BundleUtil;
import edu.harvard.iq.dataverse.consent.ConsentDto;
@@ -60,7 +61,7 @@
@ViewScoped
@Named("DataverseUserPage")
-public class DataverseUserPage implements java.io.Serializable {
+public class DataverseUserPage extends BaseUserPage {
private static final Logger logger = Logger.getLogger(DataverseUserPage.class.getCanonicalName());
diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/providers/common/BaseUserPage.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/providers/common/BaseUserPage.java
new file mode 100644
index 0000000000..7d36658f67
--- /dev/null
+++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/providers/common/BaseUserPage.java
@@ -0,0 +1,64 @@
+package edu.harvard.iq.dataverse.authorization.providers.common;
+
+import edu.harvard.iq.dataverse.common.BundleUtil;
+import edu.harvard.iq.dataverse.dataset.metadata.inputRenderer.Suggestion;
+import edu.harvard.iq.dataverse.dataset.metadata.inputRenderer.suggestion.SuggestionHandler;
+import edu.harvard.iq.dataverse.validation.OrcidValidator;
+import edu.harvard.iq.dataverse.validation.RorValidator;
+import edu.harvard.iq.dataverse.validation.field.ValidationResult;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.ejb.EJB;
+import javax.faces.application.FacesMessage;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIInput;
+import javax.faces.context.FacesContext;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Provides common functionality for the user account details page.
+ */
+public abstract class BaseUserPage implements Serializable {
+
+ @EJB
+ RorValidator rorValidator;
+
+ @EJB
+ OrcidValidator orcidValidator;
+
+ @EJB(beanName = "RorSuggestionHandler")
+ SuggestionHandler suggestionHandler;
+
+ public void validateOrcid(FacesContext context, UIComponent toValidate, Object value) {
+ String orcid = (String) value;
+ if (StringUtils.isEmpty(orcid)) {
+ return;
+ }
+ ValidationResult result = orcidValidator.validate(orcid);
+ if (!result.isOk()) {
+ ((UIInput) toValidate).setValid(false);
+ context.addMessage(toValidate.getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR,
+ BundleUtil.getStringFromBundle("user." + result.getErrorCode()), null));
+ }
+ }
+
+ public void validateAffiliationRor(FacesContext context, UIComponent toValidate, Object value) {
+ String ror = (String) value;
+ if (StringUtils.isEmpty(ror)) {
+ return;
+ }
+
+ ValidationResult result = rorValidator.validate(ror);
+ if (!result.isOk()) {
+ ((UIInput) toValidate).setValid(false);
+ context.addMessage(toValidate.getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR,
+ BundleUtil.getStringFromBundle("user.affiliationror." + result.getErrorCode()), null));
+ }
+ }
+
+ public List processAffiliationRorSuggestions(String query) {
+ return suggestionHandler.generateSuggestions(Collections.emptyMap(), query);
+ }
+}
diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/providers/common/ExternalIdpFirstLoginPage.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/providers/common/ExternalIdpFirstLoginPage.java
index 9414024fee..31896f9f97 100644
--- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/providers/common/ExternalIdpFirstLoginPage.java
+++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/authorization/providers/common/ExternalIdpFirstLoginPage.java
@@ -43,7 +43,6 @@
import javax.inject.Named;
import javax.servlet.http.HttpSession;
import java.io.IOException;
-import java.io.Serializable;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
@@ -65,7 +64,7 @@
*/
@Named("ExternalIdpFirstLoginPage")
@SessionScoped
-public class ExternalIdpFirstLoginPage implements Serializable {
+public class ExternalIdpFirstLoginPage extends BaseUserPage {
private static final Logger logger = Logger.getLogger(ExternalIdpFirstLoginPage.class.getCanonicalName());
@@ -235,7 +234,8 @@ public String init() throws IOException {
public String createNewAccount() {
AuthenticatedUserDisplayInfo displayInfo = newUser.getDisplayInfo();
AuthenticatedUserDisplayInfo newAuthenticatedUserDisplayInfo = new AuthenticatedUserDisplayInfo(
- displayInfo.getFirstName(), displayInfo.getLastName(), getSelectedEmail(), displayInfo.getAffiliation(),
+ displayInfo.getFirstName(), displayInfo.getLastName(), getSelectedEmail(), displayInfo.getOrcid(),
+ displayInfo.getAffiliation(), displayInfo.getAffiliationROR(),
displayInfo.getPosition());
final AuthenticatedUser user = authenticationSvc.createAuthenticatedUser(newUser.toUserRecordIdentifier(),
diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/OrcidValidator.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/OrcidValidator.java
new file mode 100644
index 0000000000..f398e4c2cd
--- /dev/null
+++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/OrcidValidator.java
@@ -0,0 +1,49 @@
+package edu.harvard.iq.dataverse.validation;
+
+import edu.harvard.iq.dataverse.validation.field.ValidationResult;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.ejb.Stateless;
+import java.util.Arrays;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Stateless
+public class OrcidValidator {
+
+ public static final String INVALID_FORMAT_ERROR_CODE = "orcid.invalid.format";
+ public static final String INVALID_CHECKSUM_ERROR_CODE = "orcid.invalid.checksum";
+
+ private static final Pattern ORCID_FORMAT_PATTERN = Pattern.compile("([0-9]{4})-([0-9]{4})-([0-9]{4})-([0-9]{3})([0-9X])");
+
+ // -------------------- LOGIC --------------------
+
+ public ValidationResult validate(String orcid) {
+ if (StringUtils.isEmpty(orcid)) {
+ return ValidationResult.invalid(INVALID_FORMAT_ERROR_CODE);
+ }
+
+ Matcher matcher = ORCID_FORMAT_PATTERN.matcher(orcid);
+ if (StringUtils.isBlank(orcid) || !matcher.matches()) {
+ return ValidationResult.invalid(INVALID_FORMAT_ERROR_CODE);
+ }
+
+ String encoded = matcher.group(1) + matcher.group(2) + matcher.group(3) + matcher.group(4);
+ String checksum = matcher.group(5);
+
+ return checksum.equals(computeChecksum(encoded))
+ ? ValidationResult.ok()
+ : ValidationResult.invalid(INVALID_CHECKSUM_ERROR_CODE);
+ }
+
+ // -------------------- PRIVATE --------------------
+
+ public String computeChecksum(String baseDigits) {
+ int total = Arrays.stream(baseDigits.split(""))
+ .mapToInt(Integer::parseInt)
+ .reduce(0, (accumulated, digit) -> (accumulated + digit) * 2);
+
+ int result = (12 - total % 11) % 11;
+ return result == 10 ? "X" : String.valueOf(result);
+ }
+}
diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/field/validators/RorValidator.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/RorValidator.java
similarity index 62%
rename from dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/field/validators/RorValidator.java
rename to dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/RorValidator.java
index 7c64f6f7de..656eaa9855 100644
--- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/field/validators/RorValidator.java
+++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/RorValidator.java
@@ -1,46 +1,34 @@
-package edu.harvard.iq.dataverse.validation.field.validators;
+package edu.harvard.iq.dataverse.validation;
-import edu.harvard.iq.dataverse.common.BundleUtil;
-import edu.harvard.iq.dataverse.persistence.dataset.ValidatableField;
import edu.harvard.iq.dataverse.validation.field.ValidationResult;
import org.apache.commons.lang3.StringUtils;
-import org.omnifaces.cdi.Eager;
-import javax.enterprise.context.ApplicationScoped;
+import javax.ejb.Stateless;
import java.util.Arrays;
import java.util.Collections;
-import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
-@Eager
-@ApplicationScoped
-public class RorValidator extends MultiValueValidatorBase {
-
+@Stateless
+public class RorValidator {
+ public static final String INVALID_FORMAT_ERROR_CODE = "ror.invalid.format";
+ public static final String INVALID_CHECKSUM_ERROR_CODE = "ror.invalid.checksum";
private static final Map DECODE_SYMBOL_VALUES = Initializer.initializeDecodeSymbolValues();
// -------------------- LOGIC --------------------
- @Override
- public String getName() {
- return "ror_validator";
- }
-
- @Override
- public ValidationResult validateValue(String fullRor, ValidatableField field, Map params,
- Map> fieldIndex) {
- String fieldName = field.getDatasetFieldType().getDisplayName();
+ public ValidationResult validate(String fullRor) {
if (StringUtils.isBlank(fullRor)
|| !fullRor.matches("https://ror\\.org/0[a-hjkmnp-tv-z0-9]{6}[0-9]{2}")) {
- return ValidationResult.invalid(field, BundleUtil.getStringFromBundle("ror.invalid.format", fieldName));
+ return ValidationResult.invalid(INVALID_FORMAT_ERROR_CODE);
}
String value = fullRor.substring(fullRor.lastIndexOf("/") + 1);
String encoded = value.substring(0, 7);
long checksum = Long.parseLong(value.substring(7));
return checksum == computeChecksum(encoded)
? ValidationResult.ok()
- : ValidationResult.invalid(field, BundleUtil.getStringFromBundle("ror.invalid.checksum", fieldName));
+ : ValidationResult.invalid(INVALID_CHECKSUM_ERROR_CODE);
}
// -------------------- PRIVATE --------------------
diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/field/ValidationResult.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/field/ValidationResult.java
index f43102a15e..1285e54263 100644
--- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/field/ValidationResult.java
+++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/field/ValidationResult.java
@@ -4,16 +4,19 @@
import org.apache.commons.lang3.StringUtils;
public class ValidationResult {
- public static ValidationResult OK = new ValidationResult(true,null, StringUtils.EMPTY);
- private boolean ok;
- private ValidatableField field;
- private String message;
+ public static ValidationResult OK = new ValidationResult(true, null, null, StringUtils.EMPTY);
+
+ private final boolean ok;
+ private final String errorCode;
+ private final ValidatableField field;
+ private final String message;
// -------------------- CONSTRUCTORS --------------------
- private ValidationResult(boolean ok, ValidatableField field, String validationMessage) {
+ private ValidationResult(boolean ok, String errorCode, ValidatableField field, String validationMessage) {
this.ok = ok;
+ this.errorCode = errorCode;
this.field = field;
this.message = validationMessage;
}
@@ -28,14 +31,26 @@ public String getMessage() {
return message;
}
+ public String getErrorCode() {
+ return errorCode;
+ }
+
// -------------------- LOGIC --------------------
+ public ValidationResult withInfo(ValidatableField field, String validationMessage) {
+ return new ValidationResult(ok, errorCode, field, validationMessage);
+ }
+
public static ValidationResult ok() {
return OK;
}
public static ValidationResult invalid(ValidatableField field, String message) {
- return new ValidationResult(false, field, message);
+ return new ValidationResult(false, null, field, message);
+ }
+
+ public static ValidationResult invalid(String errorCode) {
+ return new ValidationResult(false, errorCode, null, StringUtils.EMPTY);
}
public boolean isOk() {
diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/field/validators/RorFieldValidator.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/field/validators/RorFieldValidator.java
new file mode 100644
index 0000000000..8c9dc36546
--- /dev/null
+++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/validation/field/validators/RorFieldValidator.java
@@ -0,0 +1,39 @@
+package edu.harvard.iq.dataverse.validation.field.validators;
+
+import edu.harvard.iq.dataverse.common.BundleUtil;
+import edu.harvard.iq.dataverse.persistence.dataset.ValidatableField;
+import edu.harvard.iq.dataverse.validation.RorValidator;
+import edu.harvard.iq.dataverse.validation.field.ValidationResult;
+import org.omnifaces.cdi.Eager;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import java.util.List;
+import java.util.Map;
+
+@Eager
+@ApplicationScoped
+public class RorFieldValidator extends MultiValueValidatorBase {
+
+ @Inject
+ private RorValidator rorValidator;
+
+ // -------------------- LOGIC --------------------
+
+ @Override
+ public String getName() {
+ return "ror_validator";
+ }
+
+ @Override
+ public ValidationResult validateValue(String fullRor, ValidatableField field, Map params,
+ Map> fieldIndex) {
+ String fieldName = field.getDatasetFieldType().getDisplayName();
+ ValidationResult result = rorValidator.validate(fullRor);
+ if (result.isOk()) {
+ return result;
+ } else {
+ return result.withInfo(field, BundleUtil.getStringFromBundle(result.getErrorCode(), fieldName));
+ }
+ }
+}
diff --git a/dataverse-webapp/src/main/webapp/dataverseuser.xhtml b/dataverse-webapp/src/main/webapp/dataverseuser.xhtml
index 6da1236322..fbbc2e8f70 100644
--- a/dataverse-webapp/src/main/webapp/dataverseuser.xhtml
+++ b/dataverse-webapp/src/main/webapp/dataverseuser.xhtml
@@ -475,6 +475,19 @@
+
+
+
+
+
+
+
+