From 2685983a7f1f3802f1e92af9693ae31cc9c51bd3 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Fri, 3 Jan 2025 16:09:18 -0500 Subject: [PATCH 01/21] add orcid to auth user and displayinfo --- .../edu/harvard/iq/dataverse/DatasetPage.java | 2 +- .../AuthenticatedUserDisplayInfo.java | 19 +++++++++++++++++-- .../users/AuthenticatedUser.java | 19 +++++++++++-------- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java b/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java index 33a093c8044..c78fa4df05f 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java @@ -2609,7 +2609,7 @@ private void resetVersionUI() { } } - String creatorOrcidId = au.getOrcidId(); + String creatorOrcidId = au.getOrcid(); if (dsf.getDatasetFieldType().getName().equals(DatasetFieldConstant.author) && dsf.isEmpty()) { for (DatasetFieldCompoundValue authorValue : dsf.getDatasetFieldCompoundValues()) { for (DatasetField subField : authorValue.getChildDatasetFields()) { diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticatedUserDisplayInfo.java b/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticatedUserDisplayInfo.java index 0bd1a81048a..d19ed837122 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticatedUserDisplayInfo.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticatedUserDisplayInfo.java @@ -14,16 +14,21 @@ 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) { + this(firstName, lastName, emailAddress, affiliation, position, null); + } + public AuthenticatedUserDisplayInfo(String firstName, String lastName, String emailAddress, String affiliation, String position, String orcid) { super(firstName + " " + lastName,emailAddress,affiliation); this.firstName = firstName; this.lastName = lastName; - this.position = position; + this.position = position; + this.orcid = orcid; } public AuthenticatedUserDisplayInfo() { @@ -31,6 +36,7 @@ public AuthenticatedUserDisplayInfo() { firstName=""; lastName=""; position=""; + orcid=null; } @@ -39,7 +45,7 @@ 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.getAffiliation(), src.getPosition(), src.getOrcid()); } public String getLastName() { @@ -98,6 +104,15 @@ public boolean equals(Object obj) { } return Objects.equals(this.position, other.position) && super.equals(obj); } + + public void setOrcid(String orcidUrl) { + // TODO Auto-generated method stub + + } + + public String getOrcid() { + return orcid; + } } diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/users/AuthenticatedUser.java b/src/main/java/edu/harvard/iq/dataverse/authorization/users/AuthenticatedUser.java index d6d3e0317ed..b63fc78aa2c 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/users/AuthenticatedUser.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/users/AuthenticatedUser.java @@ -152,6 +152,9 @@ public class AuthenticatedUser implements User, Serializable { @Min(value = 1, message = "Rate Limit Tier must be greater than 0.") private int rateLimitTier = 1; + @Column(columnDefinition="TEXT", nullable=true) + private String orcid; + @PrePersist void prePersist() { mutedNotifications = Type.toStringValue(mutedNotificationsSet); @@ -554,14 +557,6 @@ public Timestamp getLastApiUseTime(){ return this.lastApiUseTime; } - - public String getOrcidId() { - String authProviderId = getAuthenticatedUserLookup().getAuthenticationProviderId(); - if (OrcidOAuth2AP.PROVIDER_ID_PRODUCTION.equals(authProviderId)) { - return getAuthenticatedUserLookup().getPersistentUserId(); - } - return null; - } public Cart getCart() { if (cart == null){ @@ -605,4 +600,12 @@ public boolean hasNotificationMuted(Type type) { } return this.mutedNotificationsSet.contains(type); } + + public String getOrcid() { + return orcid; + } + + public void setOrcid(String orcid) { + this.orcid = orcid; + } } From d00037f0cdb42d2594be94e37e8d2f035ad3ada9 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Fri, 3 Jan 2025 16:09:49 -0500 Subject: [PATCH 02/21] set orcid during ORCID OAuth2 signup/login --- .../oauth2/OAuth2LoginBackingBean.java | 11 ++++++++++ .../providers/oauth2/impl/OrcidOAuth2AP.java | 21 ++++++++++++++----- .../edu/harvard/iq/dataverse/api/AdminIT.java | 2 +- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/OAuth2LoginBackingBean.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/OAuth2LoginBackingBean.java index 8f3dc07fdea..08877ee572a 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/OAuth2LoginBackingBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/OAuth2LoginBackingBean.java @@ -5,6 +5,7 @@ import edu.harvard.iq.dataverse.authorization.AuthenticationProvider; import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean; import edu.harvard.iq.dataverse.authorization.UserRecordIdentifier; +import edu.harvard.iq.dataverse.authorization.providers.oauth2.impl.OrcidOAuth2AP; import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser; import edu.harvard.iq.dataverse.util.BundleUtil; import edu.harvard.iq.dataverse.util.ClockUtil; @@ -125,6 +126,9 @@ public void exchangeCodeForToken() throws IOException { signUpDisabled = true; throw new OAuth2Exception(-1, "", MessageFormat.format(BundleUtil.getStringFromBundle("oauth2.callback.error.signupDisabledForProvider"), idp.getId())); } else { + if(idp instanceof OrcidOAuth2AP) { + oauthUser.getDisplayInfo().setOrcid(((OrcidOAuth2AP)idp).getOrcidUrl(dvUser)); + } newAccountPage.setNewUser(oauthUser); Faces.redirect("/oauth2/firstLogin.xhtml"); } @@ -133,6 +137,13 @@ public void exchangeCodeForToken() throws IOException { // login the user and redirect to HOME of intended page (if any). // setUser checks for deactivated users. dvUser = userService.updateLastLogin(dvUser); + // On the first login (after this code was added) via the Orcid provider, set the user's ORCID + // Doing this here assures the user authenticated to ORCID before their profile's ORCID is set + // (and not, for example, when an account was created via API) + if((idp instanceof OrcidOAuth2AP) && dvUser.getOrcid()==null) { + dvUser.setOrcid(((OrcidOAuth2AP)idp).getOrcidUrl(dvUser)); + userService.save(dvUser); + } session.setUser(dvUser); final OAuth2TokenData tokenData = oauthUser.getTokenData(); if (tokenData != null) { diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java index 323c78ab47a..0bca145ac9a 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java @@ -12,6 +12,7 @@ import edu.harvard.iq.dataverse.authorization.providers.oauth2.OAuth2Exception; import edu.harvard.iq.dataverse.authorization.providers.oauth2.OAuth2TokenData; import edu.harvard.iq.dataverse.authorization.providers.oauth2.OAuth2UserRecord; +import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser; import edu.harvard.iq.dataverse.util.BundleUtil; import java.io.IOException; import java.io.StringReader; @@ -51,8 +52,7 @@ public class OrcidOAuth2AP extends AbstractOAuth2AuthenticationProvider { static final Logger logger = Logger.getLogger(OrcidOAuth2AP.class.getName()); - public static final String PROVIDER_ID_PRODUCTION = "orcid"; - public static final String PROVIDER_ID_SANDBOX = "orcid-sandbox"; + public static final String PROVIDER_ID = "orcid"; public OrcidOAuth2AP(String clientId, String clientSecret, String userEndpoint) { @@ -78,7 +78,7 @@ public String getUserEndpoint( OAuth2AccessToken token ) { @Override public DefaultApi20 getApiInstance() { - return OrcidApi.instance( ! baseUserEndpoint.contains("sandbox") ); + return OrcidApi.instance( isProduction() ); } @Override @@ -233,7 +233,7 @@ private NodeList xpathMatches(Document doc, String pattern) { @Override public AuthenticationProviderDisplayInfo getInfo() { - if (PROVIDER_ID_PRODUCTION.equals(getId())) { + if (isProduction()) { return new AuthenticationProviderDisplayInfo(getId(), BundleUtil.getStringFromBundle("auth.providers.title.orcid"), "ORCID user repository"); } return new AuthenticationProviderDisplayInfo(getId(), "ORCID Sandbox", "ORCID dev sandbox "); @@ -256,7 +256,10 @@ public String getPersistentIdDescription() { @Override public String getPersistentIdUrlPrefix() { - return "https://orcid.org/"; + if(isProduction()) { + return "https://orcid.org/"; + } + return "https://sandbox.orcid.org/"; } @Override @@ -329,4 +332,12 @@ protected AuthenticatedUserDisplayInfo parseActivitiesResponse( String responseB return null; } + + public String getOrcidUrl(AuthenticatedUser dvUser) { + return getPersistentIdUrlPrefix() + dvUser.getOrcid(); + } + + private boolean isProduction() { + return !baseUserEndpoint.contains("sandbox"); + } } diff --git a/src/test/java/edu/harvard/iq/dataverse/api/AdminIT.java b/src/test/java/edu/harvard/iq/dataverse/api/AdminIT.java index 94aece95861..b48c5507a54 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/AdminIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/AdminIT.java @@ -527,7 +527,7 @@ public void testConvertOAuthUserToBuiltin() throws Exception { @Test public void testCreateNonBuiltinUserViaApi() { - Response createUser = UtilIT.createRandomAuthenticatedUser(OrcidOAuth2AP.PROVIDER_ID_PRODUCTION); + Response createUser = UtilIT.createRandomAuthenticatedUser(OrcidOAuth2AP.PROVIDER_ID); createUser.prettyPrint(); assertEquals(200, createUser.getStatusCode()); From cfc53d1f43efe5e320faafc6573e72c0ee50fcdd Mon Sep 17 00:00:00 2001 From: qqmyers Date: Fri, 3 Jan 2025 16:10:09 -0500 Subject: [PATCH 03/21] display in signup/account info --- src/main/webapp/dataverseuser.xhtml | 11 +++++++++++ src/main/webapp/oauth2/firstLogin.xhtml | 15 +++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/main/webapp/dataverseuser.xhtml b/src/main/webapp/dataverseuser.xhtml index bcb71733716..2ad5dd68369 100644 --- a/src/main/webapp/dataverseuser.xhtml +++ b/src/main/webapp/dataverseuser.xhtml @@ -598,6 +598,17 @@ + + + #{DataverseUserPage.userAuthProvider.persistentIdName} + + +   + + + + + diff --git a/src/main/webapp/oauth2/firstLogin.xhtml b/src/main/webapp/oauth2/firstLogin.xhtml index 6310a0b49da..4ed69ff3276 100644 --- a/src/main/webapp/oauth2/firstLogin.xhtml +++ b/src/main/webapp/oauth2/firstLogin.xhtml @@ -111,6 +111,21 @@

+
+ +
+

+   + + + +

+
+
@@ -587,26 +593,29 @@ - + - #{DataverseUserPage.userAuthProvider.persistentIdName} + #{bundle['user.orcid']} -   - - +   + + - + + - #{DataverseUserPage.userAuthProvider.persistentIdName} + #{bundle['user.orcid']} -   - - - + + + From 8ef5ba8e78bf43f2f0d27a9fcec61bf87e4836a0 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Fri, 31 Jan 2025 14:39:44 -0500 Subject: [PATCH 10/21] callback page --- src/main/webapp/oauth2/orcidConfirm.xhtml | 72 +++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/main/webapp/oauth2/orcidConfirm.xhtml diff --git a/src/main/webapp/oauth2/orcidConfirm.xhtml b/src/main/webapp/oauth2/orcidConfirm.xhtml new file mode 100644 index 00000000000..6138f14ea29 --- /dev/null +++ b/src/main/webapp/oauth2/orcidConfirm.xhtml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + +

+ Http Return Code: + #{OAuth2Page.error.httpReturnCode} +

+ +

Raw message body:

+ +
+                    
+                    #{OAuth2Page.error.messageBody}
+                
+                
+
+ +
+
+
+ From bb4c56ae712290ce4177419428e6d3832b98bee2 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Fri, 31 Jan 2025 16:14:04 -0500 Subject: [PATCH 11/21] avoid duplicate assignments --- .../AuthenticationServiceBean.java | 21 ++++++++++++++++++- .../oauth2/OAuth2LoginBackingBean.java | 12 +++++++---- src/main/java/propertyFiles/Bundle.properties | 11 ++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationServiceBean.java index 032c1dd5164..2f6e4600721 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationServiceBean.java @@ -1043,4 +1043,23 @@ private List getAvailableOidcProviders() { .map(providerId -> (OIDCAuthProvider) getAuthenticationProvider(providerId)) .toList(); } -} + + public AuthenticatedUser lookupUserByOrcid(String orcid) { + if (orcid == null || orcid.isEmpty()) { + return null; + } + + try { + TypedQuery query = em.createQuery( + "SELECT au FROM AuthenticatedUser au WHERE au.authenticatedOrcid = :orcid", + AuthenticatedUser.class); + query.setParameter("orcid", orcid); + return query.getSingleResult(); + } catch (NoResultException e) { + return null; + } catch (NonUniqueResultException e) { + logger.log(Level.WARNING, "Multiple users found with ORCID: " + orcid, e); + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/OAuth2LoginBackingBean.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/OAuth2LoginBackingBean.java index ac36ef46a6b..7af5e8c7bf5 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/OAuth2LoginBackingBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/OAuth2LoginBackingBean.java @@ -183,10 +183,14 @@ public void setOrcidInProfile() throws IOException { UserRecordIdentifier idtf = oauthUser.getUserRecordIdentifier(); User user = session.getUser(); + String orcid = ((OrcidOAuth2AP)idp).getOrcidUrl(oauthUser.getIdInService()); + if(authenticationSvc.lookupUserByOrcid(orcid)!= null) { + throw new OAuth2Exception(-1, "", MessageFormat.format(BundleUtil.getStringFromBundle("oauth2.callback.error.orcidInUse"), orcid)); + } if(user != null && user.isAuthenticated()) { AuthenticatedUser dvUser = (AuthenticatedUser) user; if((idp instanceof OrcidOAuth2AP) && dvUser.getAuthenticatedOrcid()==null) { - dvUser.setAuthenticatedOrcid(((OrcidOAuth2AP)idp).getOrcidUrl(oauthUser.getIdInService())); + dvUser.setAuthenticatedOrcid(orcid); userService.save(dvUser); } session.setUser(dvUser); @@ -194,16 +198,16 @@ public void setOrcidInProfile() throws IOException { Faces.redirect(redirectPage.orElse("/")); } else { - throw new OAuth2Exception(-1, "", MessageFormat.format(BundleUtil.getStringFromBundle("oauth2.callback.error.signupDisabledForProvider"), idp.getId())); + throw new OAuth2Exception(-1, "", MessageFormat.format(BundleUtil.getStringFromBundle("oauth2.callback.error.accountNotFound"), idp.getId())); } } } catch (OAuth2Exception ex) { error = ex; - logger.log(Level.INFO, "OAuth2Exception caught. HTTP return code: {0}. Message: {1}. Message body: {2}", new Object[]{error.getHttpReturnCode(), error.getLocalizedMessage(), error.getMessageBody()}); + logger.log(Level.INFO, "ORCID OAuth2Exception caught. HTTP return code: {0}. Message: {1}. Message body: {2}", new Object[]{error.getHttpReturnCode(), error.getLocalizedMessage(), error.getMessageBody()}); Logger.getLogger(OAuth2LoginBackingBean.class.getName()).log(Level.SEVERE, null, ex); } catch (InterruptedException | ExecutionException ex) { - error = new OAuth2Exception(-1, "Please see server logs for more details", "Could not login due to threading exceptions."); + error = new OAuth2Exception(-1, "Please see server logs for more details", "Could not login at ORCID due to threading exceptions."); logger.log(Level.WARNING, "Threading exception caught. Message: {0}", ex.getLocalizedMessage()); } } diff --git a/src/main/java/propertyFiles/Bundle.properties b/src/main/java/propertyFiles/Bundle.properties index 9ae491b123e..503ff4ddd4b 100644 --- a/src/main/java/propertyFiles/Bundle.properties +++ b/src/main/java/propertyFiles/Bundle.properties @@ -215,6 +215,13 @@ user.helpShibUserMigrateOffShibAfterLink=for assistance. user.helpOAuthBeforeLink=Your Dataverse account uses {0} for login. If you are interested in changing login methods, please contact user.helpOAuthAfterLink=for assistance. user.lostPasswdTip=If you have lost or forgotten your password, please enter your username or email address below and click Submit. We will send you an e-mail with your new password. + +user.orcid=ORCID +user.orcid.link=Link to ORCID profile +user.orcid.authenticate=Add Authenticated ORCID +user.orcid.remove=Remove ORCID +user.orcid.callback.message=Authentication Error - Dataverse could not authenticate your login at ORCID and will not add your ORCID to your profile. Please make sure you authorize your account to connect with Dataverse. + user.dataRelatedToMe=My Data wasCreatedIn=, was created in wasCreatedTo=, was added to @@ -513,6 +520,8 @@ oauth2.callback.page.title=OAuth Callback oauth2.callback.message=Authentication Error - Dataverse could not authenticate your login with the provider that you selected. Please make sure you authorize your account to connect with Dataverse. For more details about the information being requested, see the User Guide. oauth2.callback.error.providerDisabled=This authentication method ({0}) is currently disabled. Please log in using one of the supported methods. oauth2.callback.error.signupDisabledForProvider=Sorry, signup for new accounts using {0} authentication is currently disabled. +oauth2.callback.error.accountNotFound=You must be logged in to associate an ORCID with your account. +oauth2.callback.error.orcidInUse=This ORCID ({0}) is already associated with another user account. # deactivated user accounts deactivated.error=Sorry, your account has been deactivated. @@ -2503,6 +2512,8 @@ permission.addDatasetDataverse=Add a dataset to a dataverse userPage.informationUpdated=Your account information has been successfully updated. userPage.passwordChanged=Your account password has been successfully changed. confirmEmail.changed=Your email address has changed and must be re-verified. Please check your inbox at {0} and follow the link we''ve sent. \n\nAlso, please note that the link will only work for the next {1} before it has expired. +auth.orcid.notConfigured=ORCID authentication is not configured. +auth.orcid.error=An error occurred while starting ORCID authentication. Please try again later. #Dataset.java dataset.category.documentation=Documentation From 211fe5627a04c67ed6c0de8b3cdf03bb9045392b Mon Sep 17 00:00:00 2001 From: qqmyers Date: Fri, 31 Jan 2025 16:14:43 -0500 Subject: [PATCH 12/21] don't link to remote providers if not allowed for signup --- src/main/java/Bundle.properties | 0 .../iq/dataverse/authorization/AuthUtil.java | 9 +++- .../providers/builtin/DataverseUserPage.java | 2 +- .../providers/oauth2/impl/OrcidOAuth2AP.java | 4 +- .../dataverse/authorization/AuthUtilTest.java | 44 +++++++++++++++---- 5 files changed, 45 insertions(+), 14 deletions(-) create mode 100644 src/main/java/Bundle.properties diff --git a/src/main/java/Bundle.properties b/src/main/java/Bundle.properties new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/AuthUtil.java b/src/main/java/edu/harvard/iq/dataverse/authorization/AuthUtil.java index 27143199b5b..01f1cbdf0a3 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/AuthUtil.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/AuthUtil.java @@ -3,6 +3,8 @@ import edu.harvard.iq.dataverse.authorization.providers.builtin.DataverseUserPage; import edu.harvard.iq.dataverse.authorization.providers.oauth2.AbstractOAuth2AuthenticationProvider; import edu.harvard.iq.dataverse.authorization.providers.shib.ShibAuthenticationProvider; +import edu.harvard.iq.dataverse.util.SystemConfig; + import java.util.Collection; import java.util.logging.Logger; @@ -10,12 +12,15 @@ public class AuthUtil { private static final Logger logger = Logger.getLogger(DataverseUserPage.class.getCanonicalName()); - public static boolean isNonLocalLoginEnabled(Collection providers) { + public static boolean isNonLocalSignupEnabled(Collection providers, SystemConfig systemConfig) { if (providers != null) { + for (AuthenticationProvider provider : providers) { if (provider instanceof AbstractOAuth2AuthenticationProvider || provider instanceof ShibAuthenticationProvider) { logger.fine("found an remote auth provider (returning true): " + provider.getId()); - return true; + if(!systemConfig.isSignupDisabledForRemoteAuthProvider(provider.getId())) { + return true; + } } else { logger.fine("not a remote auth provider: " + provider.getId()); } diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/builtin/DataverseUserPage.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/builtin/DataverseUserPage.java index 30a4c235645..21c4aa2f2a3 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/builtin/DataverseUserPage.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/builtin/DataverseUserPage.java @@ -721,7 +721,7 @@ public void setUsername(String username) { } public boolean isNonLocalLoginEnabled() { - return AuthUtil.isNonLocalLoginEnabled(authenticationService.getAuthenticationProviders()); + return AuthUtil.isNonLocalSignupEnabled(authenticationService.getAuthenticationProviders(), systemConfig); } public String getReasonForReturn(DatasetVersion datasetVersion) { diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java index 53ff23124a3..d106ad45398 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java @@ -55,7 +55,7 @@ public class OrcidOAuth2AP extends AbstractOAuth2AuthenticationProvider { public static final String PROVIDER_ID = "orcid"; public OrcidOAuth2AP(String clientId, String clientSecret, String userEndpoint) { - + this.id=PROVIDER_ID; if(userEndpoint != null && userEndpoint.startsWith("https://pub")) { this.scope = Arrays.asList("/authenticate"); } else { @@ -241,7 +241,7 @@ public AuthenticationProviderDisplayInfo getInfo() { @Override public boolean isDisplayIdentifier() { - return true; + return false; } @Override diff --git a/src/test/java/edu/harvard/iq/dataverse/authorization/AuthUtilTest.java b/src/test/java/edu/harvard/iq/dataverse/authorization/AuthUtilTest.java index 74f6d714a5e..ff8c4beb6a5 100644 --- a/src/test/java/edu/harvard/iq/dataverse/authorization/AuthUtilTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/authorization/AuthUtilTest.java @@ -5,18 +5,35 @@ import edu.harvard.iq.dataverse.authorization.providers.oauth2.impl.GoogleOAuth2AP; import edu.harvard.iq.dataverse.authorization.providers.oauth2.impl.OrcidOAuth2AP; import edu.harvard.iq.dataverse.authorization.providers.shib.ShibAuthenticationProvider; +import edu.harvard.iq.dataverse.util.SystemConfig; import java.util.Collection; import java.util.HashSet; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.*; public class AuthUtilTest { + + @Mock + private SystemConfig systemConfig; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + + // Configure the mock SystemConfig + when(systemConfig.isSignupDisabledForRemoteAuthProvider(anyString())).thenReturn(false); + when(systemConfig.isSignupDisabledForRemoteAuthProvider("orcid")).thenReturn(true); + } @ParameterizedTest @CsvSource(value = { @@ -34,28 +51,37 @@ void testGetDisplayName(String expectedDisplayName, String displayFirst, String * Test of isNonLocalLoginEnabled method, of class AuthUtil. */ @Test - public void testIsNonLocalLoginEnabled() { - System.out.println("isNonLocalLoginEnabled"); - - AuthUtil authUtil = new AuthUtil(); + public void testIsNonLocalSignupEnabled() { + System.out.println("isNonLocalSignupEnabled"); - assertFalse(AuthUtil.isNonLocalLoginEnabled(null)); + assertFalse(AuthUtil.isNonLocalSignupEnabled(null, systemConfig)); Collection shibOnly = new HashSet<>(); shibOnly.add(new ShibAuthenticationProvider()); - assertTrue(AuthUtil.isNonLocalLoginEnabled(shibOnly)); + assertTrue(AuthUtil.isNonLocalSignupEnabled(shibOnly, systemConfig)); + + Collection orcidOnly = new HashSet<>(); + orcidOnly.add(new OrcidOAuth2AP(null, null, null)); + assertFalse(AuthUtil.isNonLocalSignupEnabled(orcidOnly, systemConfig)); + Collection manyNonLocal = new HashSet<>(); manyNonLocal.add(new ShibAuthenticationProvider()); manyNonLocal.add(new GitHubOAuth2AP(null, null)); manyNonLocal.add(new GoogleOAuth2AP(null, null)); manyNonLocal.add(new OrcidOAuth2AP(null, null, null)); - assertTrue(AuthUtil.isNonLocalLoginEnabled(manyNonLocal)); + assertTrue(AuthUtil.isNonLocalSignupEnabled(manyNonLocal, systemConfig)); Collection onlyBuiltin = new HashSet<>(); onlyBuiltin.add(new BuiltinAuthenticationProvider(null, null, null)); // only builtin provider - assertFalse(AuthUtil.isNonLocalLoginEnabled(onlyBuiltin)); + assertFalse(AuthUtil.isNonLocalSignupEnabled(onlyBuiltin, systemConfig)); } -} + @Test + public void testIsSignupDisabledForRemoteAuthProvider() { + assertTrue(systemConfig.isSignupDisabledForRemoteAuthProvider("orcid")); + assertFalse(systemConfig.isSignupDisabledForRemoteAuthProvider("github")); + assertFalse(systemConfig.isSignupDisabledForRemoteAuthProvider("google")); + } +} \ No newline at end of file From 84f6fc7e7d363eb3fc71ef1ce5c1ee32f505102a Mon Sep 17 00:00:00 2001 From: qqmyers Date: Fri, 31 Jan 2025 17:50:09 -0500 Subject: [PATCH 13/21] remove unused methods --- .../authorization/AuthenticationProvider.java | 10 ------- .../BuiltinAuthenticationProvider.java | 5 ---- .../providers/oauth2/impl/GitHubOAuth2AP.java | 26 ------------------- .../providers/oauth2/impl/GoogleOAuth2AP.java | 6 ----- .../providers/oauth2/impl/OrcidOAuth2AP.java | 19 +------------- .../oauth2/oidc/OIDCAuthProvider.java | 12 --------- .../shib/ShibAuthenticationProvider.java | 5 ---- 7 files changed, 1 insertion(+), 82 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationProvider.java b/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationProvider.java index 2b137ca0dec..8dfe7ca86b3 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationProvider.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationProvider.java @@ -33,16 +33,6 @@ public interface AuthenticationProvider { default boolean isUserInfoUpdateAllowed() { return false; }; default boolean isUserDeletionAllowed() { return false; }; default boolean isOAuthProvider() { return false; }; - /** @todo Consider moving some or all of these to AuthenticationProviderDisplayInfo.*/ - /** The identifier is only displayed in the UI if it's meaningful, such as an ORCID iD.*/ - default boolean isDisplayIdentifier() { return false; }; - /** ORCID calls their persistent id an "ORCID iD".*/ - default String getPersistentIdName() { return null; }; - /** ORCID has special language to describe their ID: http://members.orcid.org/logos-web-graphics */ - default String getPersistentIdDescription() { return null; }; - /** An ORCID example would be the "http://orcid.org/" part of http://orcid.org/0000-0002-7874-374X*/ - default String getPersistentIdUrlPrefix() { return null; }; - default String getLogo() { return null; }; diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/builtin/BuiltinAuthenticationProvider.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/builtin/BuiltinAuthenticationProvider.java index c9edd04cc1e..94de21a88b9 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/builtin/BuiltinAuthenticationProvider.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/builtin/BuiltinAuthenticationProvider.java @@ -156,9 +156,4 @@ public boolean isOAuthProvider() { return false; } - @Override - public boolean isDisplayIdentifier() { - return false; - } - } diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/GitHubOAuth2AP.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/GitHubOAuth2AP.java index 8829a25336b..5b201ce77db 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/GitHubOAuth2AP.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/GitHubOAuth2AP.java @@ -58,30 +58,4 @@ protected ParsedUserResponse parseUserResponse( String responseBody ) { } } - - @Override - public boolean isDisplayIdentifier() { - return false; - } - - @Override - public String getPersistentIdName() { - return BundleUtil.getStringFromBundle("auth.providers.persistentUserIdName.github"); - } - - @Override - public String getPersistentIdDescription() { - return BundleUtil.getStringFromBundle("auth.providers.persistentUserIdTooltip.github"); - } - - @Override - public String getPersistentIdUrlPrefix() { - return null; - } - - @Override - public String getLogo() { - return null; - } - } diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/GoogleOAuth2AP.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/GoogleOAuth2AP.java index a864ecb810a..913eb038d8e 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/GoogleOAuth2AP.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/GoogleOAuth2AP.java @@ -63,10 +63,4 @@ protected ParsedUserResponse parseUserResponse(String responseBody) { return new ParsedUserResponse(displayInfo, persistentUserId, username); } } - - @Override - public boolean isDisplayIdentifier() { - return false; - } - } diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java index d106ad45398..ddf527b95a7 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java @@ -239,22 +239,6 @@ public AuthenticationProviderDisplayInfo getInfo() { return new AuthenticationProviderDisplayInfo(getId(), "ORCID Sandbox", "ORCID dev sandbox "); } - @Override - public boolean isDisplayIdentifier() { - return false; - } - - @Override - public String getPersistentIdName() { - return BundleUtil.getStringFromBundle("auth.providers.persistentUserIdName.orcid"); - } - - @Override - public String getPersistentIdDescription() { - return BundleUtil.getStringFromBundle("auth.providers.persistentUserIdTooltip.orcid"); - } - - @Override public String getPersistentIdUrlPrefix() { if(isProduction()) { return "https://orcid.org/"; @@ -262,8 +246,7 @@ public String getPersistentIdUrlPrefix() { return "https://sandbox.orcid.org/"; } - @Override - public String getLogo() { + public final static String getLogo() { return "/resources/images/orcid_16x16.png"; } diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/oidc/OIDCAuthProvider.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/oidc/OIDCAuthProvider.java index f396ebf6487..5cf8ca2ea55 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/oidc/OIDCAuthProvider.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/oidc/OIDCAuthProvider.java @@ -94,18 +94,6 @@ public OIDCAuthProvider(String aClientId, String aClientSecret, String issuerEnd this.pkceMethod = CodeChallengeMethod.parse(pkceMethod); } - /** - * Although this is defined in {@link edu.harvard.iq.dataverse.authorization.AuthenticationProvider}, - * this needs to be present due to bugs in ELResolver (has been modified for Spring). - * TODO: for the future it might be interesting to make this configurable via the provider JSON (it's used for ORCID!) - * @see JBoss Issue 159 - * @see Jakarta EE Bug 43 - * @return false - */ - @Override - public boolean isDisplayIdentifier() { - return false; - } /** * Setup metadata from OIDC provider during creation of the provider representation diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/shib/ShibAuthenticationProvider.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/shib/ShibAuthenticationProvider.java index e7dccc34300..0f6be352ed9 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/shib/ShibAuthenticationProvider.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/shib/ShibAuthenticationProvider.java @@ -28,11 +28,6 @@ public boolean isOAuthProvider() { return false; } - @Override - public boolean isDisplayIdentifier() { - return false; - } - // We don't override "isEmailVerified" because we're using timestamps // ("emailconfirmed" on the "authenticateduser" table) to know if // Shib users have confirmed/verified their email or not. From 0b0a72e33995cdcc58166fd0aee97de3bf1620ff Mon Sep 17 00:00:00 2001 From: qqmyers Date: Fri, 31 Jan 2025 17:50:43 -0500 Subject: [PATCH 14/21] fix logos, add orcid to firstLogin page --- .../AuthenticatedUserDisplayInfo.java | 16 ++++++++-- src/main/webapp/dataverseuser.xhtml | 18 ++--------- src/main/webapp/loginpage.xhtml | 7 +++-- src/main/webapp/oauth2/firstLogin.xhtml | 30 +++++-------------- 4 files changed, 28 insertions(+), 43 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticatedUserDisplayInfo.java b/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticatedUserDisplayInfo.java index d19ed837122..94871c89083 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticatedUserDisplayInfo.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticatedUserDisplayInfo.java @@ -106,13 +106,25 @@ public boolean equals(Object obj) { } public void setOrcid(String orcidUrl) { - // TODO Auto-generated method stub - + this.orcid=orcidUrl; } public String getOrcid() { return orcid; } + public String getOrcidForDisplay() { + String orcidUrl = getOrcid(); + if(orcidUrl == null) { + return null; + } + int index = orcidUrl.lastIndexOf('/'); + if (index > 0) { + return orcidUrl.substring(index + 1); + } else { + return orcidUrl; + } + } + } diff --git a/src/main/webapp/dataverseuser.xhtml b/src/main/webapp/dataverseuser.xhtml index e7676ca885b..78d4253244c 100644 --- a/src/main/webapp/dataverseuser.xhtml +++ b/src/main/webapp/dataverseuser.xhtml @@ -598,7 +598,8 @@ #{bundle['user.orcid']} -   + +   @@ -815,21 +816,6 @@ -
- - - - -
-

-   - - - -

-
-
diff --git a/src/main/webapp/loginpage.xhtml b/src/main/webapp/loginpage.xhtml index e1b77e9583b..d678b962349 100644 --- a/src/main/webapp/loginpage.xhtml +++ b/src/main/webapp/loginpage.xhtml @@ -185,9 +185,10 @@
- - - + + + +

diff --git a/src/main/webapp/oauth2/firstLogin.xhtml b/src/main/webapp/oauth2/firstLogin.xhtml index 4ed69ff3276..07b1631d6cb 100644 --- a/src/main/webapp/oauth2/firstLogin.xhtml +++ b/src/main/webapp/oauth2/firstLogin.xhtml @@ -5,6 +5,7 @@ xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" xmlns:jsf="http://xmlns.jcp.org/jsf" + xmlns:o="http://omnifaces.org/ui" xmlns:of="http://omnifaces.org/functions"> @@ -95,33 +96,18 @@

-
- -
-

-   - - - - -

-
-
-