From 957e41d9a6ebf80406962bae5fa2a8453fd50982 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Wed, 1 Apr 2026 16:52:46 -0400 Subject: [PATCH 1/7] Update shortcut URL page with extra warning text --- src/main/resources/messages.properties | 1 + src/main/resources/templates/casInstitutionLoginView.html | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 25aa8bb5..1ced7a12 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -611,6 +611,7 @@ screen.institutionlogin.message.auto=Your institution has partnered with OSF. Pl screen.institutionlogin.heading.select=Your institution screen.institutionlogin.heading.auto=Your institution screen.institutionlogin.link.select=Not your institution? +screen.institutionlogin.link.hidden=placeholder: here goes some warning message screen.institutionlogin.link.unsupported=I can't find my institution screen.institutionlogin.button.submit=Sign in screen.institutionlogin.osf=Sign in with email diff --git a/src/main/resources/templates/casInstitutionLoginView.html b/src/main/resources/templates/casInstitutionLoginView.html index bdf9f08e..087497aa 100644 --- a/src/main/resources/templates/casInstitutionLoginView.html +++ b/src/main/resources/templates/casInstitutionLoginView.html @@ -31,6 +31,10 @@

+
+

+
+
From 779687cac509dd8e1cd8a8ae208ccd7c8c22498e Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Thu, 2 Apr 2026 16:21:22 -0400 Subject: [PATCH 2/7] Update login flow & context to only show warning for hidden SSO --- .../osf/authentication/support/OsfInstitutionUtils.java | 7 +++++++ .../web/flow/login/OsfAbstractLoginPreparationAction.java | 2 ++ .../web/flow/login/OsfDefaultLoginPreparationAction.java | 2 ++ .../flow/login/OsfInstitutionLoginPreparationAction.java | 3 +++ .../io/cos/cas/osf/web/support/OsfCasLoginContext.java | 2 ++ src/main/resources/templates/casInstitutionLoginView.html | 2 +- 6 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/cos/cas/osf/authentication/support/OsfInstitutionUtils.java b/src/main/java/io/cos/cas/osf/authentication/support/OsfInstitutionUtils.java index c749f363..195608be 100644 --- a/src/main/java/io/cos/cas/osf/authentication/support/OsfInstitutionUtils.java +++ b/src/main/java/io/cos/cas/osf/authentication/support/OsfInstitutionUtils.java @@ -31,6 +31,13 @@ public static boolean validateInstitutionForLogin(final JpaOsfDao jpaOsfDao, fin && institution.getSsoAvailability() != SsoAvailability.UNAVAILABLE; } + public static boolean isInstitutionSsoAvailabilityHidden(final JpaOsfDao jpaOsfDao, final String id) { + final OsfInstitution institution = jpaOsfDao.findOneInstitutionById(id); + return institution != null + && institution.getDelegationProtocol() != null + && institution.getSsoAvailability() == SsoAvailability.HIDDEN; + } + public static String getInstitutionSupportEmail(final JpaOsfDao jpaOsfDao, final String id) { final OsfInstitution institution = jpaOsfDao.findOneInstitutionById(id); return institution != null ? institution.getSupportEmail() : null; diff --git a/src/main/java/io/cos/cas/osf/web/flow/login/OsfAbstractLoginPreparationAction.java b/src/main/java/io/cos/cas/osf/web/flow/login/OsfAbstractLoginPreparationAction.java index 9751e49f..5292fb4e 100644 --- a/src/main/java/io/cos/cas/osf/web/flow/login/OsfAbstractLoginPreparationAction.java +++ b/src/main/java/io/cos/cas/osf/web/flow/login/OsfAbstractLoginPreparationAction.java @@ -37,6 +37,8 @@ public abstract class OsfAbstractLoginPreparationAction extends AbstractAuthenti protected static final String PARAMETER_INSTITUTION_ID = "institutionId"; + protected static final String PARAMETER_HIDDEN_SSO_AVAILABILITY = "hiddenSsoAvailability"; + protected static final String PARAMETER_ORCID_CLIENT_TYPE = "orcid"; protected static final String PARAMETER_CAS_CLIENT_TYPE = "cas"; diff --git a/src/main/java/io/cos/cas/osf/web/flow/login/OsfDefaultLoginPreparationAction.java b/src/main/java/io/cos/cas/osf/web/flow/login/OsfDefaultLoginPreparationAction.java index 280718a4..f5c608b8 100644 --- a/src/main/java/io/cos/cas/osf/web/flow/login/OsfDefaultLoginPreparationAction.java +++ b/src/main/java/io/cos/cas/osf/web/flow/login/OsfDefaultLoginPreparationAction.java @@ -70,6 +70,7 @@ protected Event doExecute(RequestContext context) { loginContext = new OsfCasLoginContext( institutionLogin, institutionId, + Boolean.FALSE, StringUtils.EMPTY, unsupportedInstitutionLogin, orcidRedirect, @@ -80,6 +81,7 @@ protected Event doExecute(RequestContext context) { } else { loginContext.setInstitutionLogin(institutionLogin); loginContext.setInstitutionId(institutionId); + loginContext.setHiddenSsoAvailability(false); loginContext.setInstitutionSupportEmail(StringUtils.EMPTY); loginContext.setUnsupportedInstitutionLogin(unsupportedInstitutionLogin); loginContext.setOrcidLoginUrl(orcidLoginUrl); diff --git a/src/main/java/io/cos/cas/osf/web/flow/login/OsfInstitutionLoginPreparationAction.java b/src/main/java/io/cos/cas/osf/web/flow/login/OsfInstitutionLoginPreparationAction.java index 142b1785..169e3814 100644 --- a/src/main/java/io/cos/cas/osf/web/flow/login/OsfInstitutionLoginPreparationAction.java +++ b/src/main/java/io/cos/cas/osf/web/flow/login/OsfInstitutionLoginPreparationAction.java @@ -81,6 +81,9 @@ protected Event doExecute(RequestContext context) { context.getFlowScope().put(PARAMETER_LOGIN_CONTEXT, loginContext); institutionId = null; } else { + if (OsfInstitutionUtils.isInstitutionSsoAvailabilityHidden(jpaOsfDao, institutionId)) { + loginContext.setHiddenSsoAvailability(true); + } final String institutionSupportEmail = OsfInstitutionUtils.getInstitutionSupportEmail(jpaOsfDao, institutionId); if (institutionSupportEmail != null) { loginContext.setInstitutionSupportEmail(institutionSupportEmail); diff --git a/src/main/java/io/cos/cas/osf/web/support/OsfCasLoginContext.java b/src/main/java/io/cos/cas/osf/web/support/OsfCasLoginContext.java index d0679497..e7f476dc 100644 --- a/src/main/java/io/cos/cas/osf/web/support/OsfCasLoginContext.java +++ b/src/main/java/io/cos/cas/osf/web/support/OsfCasLoginContext.java @@ -30,6 +30,8 @@ public class OsfCasLoginContext implements Serializable { private String institutionId; + private boolean hiddenSsoAvailability; + private String institutionSupportEmail; private boolean unsupportedInstitutionLogin; diff --git a/src/main/resources/templates/casInstitutionLoginView.html b/src/main/resources/templates/casInstitutionLoginView.html index 087497aa..f482072b 100644 --- a/src/main/resources/templates/casInstitutionLoginView.html +++ b/src/main/resources/templates/casInstitutionLoginView.html @@ -32,7 +32,7 @@

-

+

From a72122b3c7896f3cd06947758d167d6585b7723c Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Thu, 2 Apr 2026 17:12:55 -0400 Subject: [PATCH 3/7] Replace placeholder message with microcopy from PO --- src/main/resources/messages.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 1ced7a12..d602ae13 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -611,7 +611,7 @@ screen.institutionlogin.message.auto=Your institution has partnered with OSF. Pl screen.institutionlogin.heading.select=Your institution screen.institutionlogin.heading.auto=Your institution screen.institutionlogin.link.select=Not your institution? -screen.institutionlogin.link.hidden=placeholder: here goes some warning message +screen.institutionlogin.link.hidden=Note: Your institution is currently being set up and may not be available. Please check back at a later date. screen.institutionlogin.link.unsupported=I can't find my institution screen.institutionlogin.button.submit=Sign in screen.institutionlogin.osf=Sign in with email From c723a40e618a5987177d69b7990d01d94332f238 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Mon, 6 Apr 2026 11:56:03 -0400 Subject: [PATCH 4/7] Reduce code duplication in institution flow and utils --- .../authentication/support/OsfInstitutionUtils.java | 10 +--------- .../login/OsfInstitutionLoginPreparationAction.java | 8 +++++--- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/cos/cas/osf/authentication/support/OsfInstitutionUtils.java b/src/main/java/io/cos/cas/osf/authentication/support/OsfInstitutionUtils.java index 195608be..b575efe5 100644 --- a/src/main/java/io/cos/cas/osf/authentication/support/OsfInstitutionUtils.java +++ b/src/main/java/io/cos/cas/osf/authentication/support/OsfInstitutionUtils.java @@ -24,20 +24,12 @@ public final class OsfInstitutionUtils { public final static String ORCID_SUFFIX = " (via ORCiD SSO)"; - public static boolean validateInstitutionForLogin(final JpaOsfDao jpaOsfDao, final String id) { - final OsfInstitution institution = jpaOsfDao.findOneInstitutionById(id); + public static boolean validateInstitutionForLogin(final OsfInstitution institution) { return institution != null && institution.getDelegationProtocol() != null && institution.getSsoAvailability() != SsoAvailability.UNAVAILABLE; } - public static boolean isInstitutionSsoAvailabilityHidden(final JpaOsfDao jpaOsfDao, final String id) { - final OsfInstitution institution = jpaOsfDao.findOneInstitutionById(id); - return institution != null - && institution.getDelegationProtocol() != null - && institution.getSsoAvailability() == SsoAvailability.HIDDEN; - } - public static String getInstitutionSupportEmail(final JpaOsfDao jpaOsfDao, final String id) { final OsfInstitution institution = jpaOsfDao.findOneInstitutionById(id); return institution != null ? institution.getSupportEmail() : null; diff --git a/src/main/java/io/cos/cas/osf/web/flow/login/OsfInstitutionLoginPreparationAction.java b/src/main/java/io/cos/cas/osf/web/flow/login/OsfInstitutionLoginPreparationAction.java index 169e3814..a530171f 100644 --- a/src/main/java/io/cos/cas/osf/web/flow/login/OsfInstitutionLoginPreparationAction.java +++ b/src/main/java/io/cos/cas/osf/web/flow/login/OsfInstitutionLoginPreparationAction.java @@ -2,6 +2,7 @@ import io.cos.cas.osf.authentication.support.OsfInstitutionUtils; import io.cos.cas.osf.dao.JpaOsfDao; +import io.cos.cas.osf.model.OsfInstitution; import io.cos.cas.osf.web.support.OsfCasLoginContext; import lombok.extern.slf4j.Slf4j; @@ -76,15 +77,16 @@ protected Event doExecute(RequestContext context) { -> (OsfCasLoginContext) requestContext.getFlowScope().get(PARAMETER_LOGIN_CONTEXT)).orElse(null); if (loginContext != null) { institutionId = loginContext.getInstitutionId(); - if (!OsfInstitutionUtils.validateInstitutionForLogin(jpaOsfDao, institutionId)) { + final OsfInstitution institution = jpaOsfDao.findOneInstitutionById(institutionId); + if (!OsfInstitutionUtils.validateInstitutionForLogin(institution)) { loginContext.setInstitutionId(null); context.getFlowScope().put(PARAMETER_LOGIN_CONTEXT, loginContext); institutionId = null; } else { - if (OsfInstitutionUtils.isInstitutionSsoAvailabilityHidden(jpaOsfDao, institutionId)) { + if (institution.getSsoAvailability().isHidden()) { loginContext.setHiddenSsoAvailability(true); } - final String institutionSupportEmail = OsfInstitutionUtils.getInstitutionSupportEmail(jpaOsfDao, institutionId); + final String institutionSupportEmail = institution.getSupportEmail(); if (institutionSupportEmail != null) { loginContext.setInstitutionSupportEmail(institutionSupportEmail); } From 0c77b7cdc7dc9276cf149856393c1c969175d927 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Mon, 6 Apr 2026 11:57:47 -0400 Subject: [PATCH 5/7] Remove unused params --- .../osf/web/flow/login/OsfAbstractLoginPreparationAction.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/io/cos/cas/osf/web/flow/login/OsfAbstractLoginPreparationAction.java b/src/main/java/io/cos/cas/osf/web/flow/login/OsfAbstractLoginPreparationAction.java index 5292fb4e..bc72df02 100644 --- a/src/main/java/io/cos/cas/osf/web/flow/login/OsfAbstractLoginPreparationAction.java +++ b/src/main/java/io/cos/cas/osf/web/flow/login/OsfAbstractLoginPreparationAction.java @@ -27,8 +27,6 @@ public abstract class OsfAbstractLoginPreparationAction extends AbstractAuthenti protected static final String PARAMETER_LOGIN_CONTEXT = "osfCasLoginContext"; - protected static final String PARAMETER_SERVICE = "service"; - protected static final String PARAMETER_CAMPAIGN = "campaign"; protected static final String PARAMETER_CAMPAIGN_VALUE = "institution"; @@ -37,8 +35,6 @@ public abstract class OsfAbstractLoginPreparationAction extends AbstractAuthenti protected static final String PARAMETER_INSTITUTION_ID = "institutionId"; - protected static final String PARAMETER_HIDDEN_SSO_AVAILABILITY = "hiddenSsoAvailability"; - protected static final String PARAMETER_ORCID_CLIENT_TYPE = "orcid"; protected static final String PARAMETER_CAS_CLIENT_TYPE = "cas"; From 2b32f721a1c90028fa3f54749c6d373f6900c816 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Mon, 6 Apr 2026 15:57:33 -0400 Subject: [PATCH 6/7] Fix login context update --- .../flow/login/OsfInstitutionLoginPreparationAction.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/cos/cas/osf/web/flow/login/OsfInstitutionLoginPreparationAction.java b/src/main/java/io/cos/cas/osf/web/flow/login/OsfInstitutionLoginPreparationAction.java index a530171f..3624881f 100644 --- a/src/main/java/io/cos/cas/osf/web/flow/login/OsfInstitutionLoginPreparationAction.java +++ b/src/main/java/io/cos/cas/osf/web/flow/login/OsfInstitutionLoginPreparationAction.java @@ -80,17 +80,17 @@ protected Event doExecute(RequestContext context) { final OsfInstitution institution = jpaOsfDao.findOneInstitutionById(institutionId); if (!OsfInstitutionUtils.validateInstitutionForLogin(institution)) { loginContext.setInstitutionId(null); - context.getFlowScope().put(PARAMETER_LOGIN_CONTEXT, loginContext); institutionId = null; } else { - if (institution.getSsoAvailability().isHidden()) { - loginContext.setHiddenSsoAvailability(true); - } final String institutionSupportEmail = institution.getSupportEmail(); if (institutionSupportEmail != null) { loginContext.setInstitutionSupportEmail(institutionSupportEmail); } + if (institution.getSsoAvailability().isHidden()) { + loginContext.setHiddenSsoAvailability(true); + } } + context.getFlowScope().put(PARAMETER_LOGIN_CONTEXT, loginContext); } final Map institutionLoginUrlMap From 56e498af10d9fe9d45e363e87288d770b3e576a8 Mon Sep 17 00:00:00 2001 From: Longze Chen Date: Mon, 6 Apr 2026 16:12:03 -0400 Subject: [PATCH 7/7] Add/Update JavaDoc --- .../support/OsfInstitutionUtils.java | 25 +++++++++++++++++-- .../osf/web/support/OsfCasLoginContext.java | 14 +++++++---- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/cos/cas/osf/authentication/support/OsfInstitutionUtils.java b/src/main/java/io/cos/cas/osf/authentication/support/OsfInstitutionUtils.java index b575efe5..0edc7547 100644 --- a/src/main/java/io/cos/cas/osf/authentication/support/OsfInstitutionUtils.java +++ b/src/main/java/io/cos/cas/osf/authentication/support/OsfInstitutionUtils.java @@ -24,17 +24,32 @@ public final class OsfInstitutionUtils { public final static String ORCID_SUFFIX = " (via ORCiD SSO)"; + /** + * @param institution the OSF institution to verify + * @return whether the given institution is eligible for institution SSO. + */ public static boolean validateInstitutionForLogin(final OsfInstitution institution) { return institution != null && institution.getDelegationProtocol() != null && institution.getSsoAvailability() != SsoAvailability.UNAVAILABLE; } - public static String getInstitutionSupportEmail(final JpaOsfDao jpaOsfDao, final String id) { - final OsfInstitution institution = jpaOsfDao.findOneInstitutionById(id); + /** + * @param jpaOsfDao the data access object for OSF DB + * @param institutionId the institution ID + * @return the institution's support email if exists + */ + public static String getInstitutionSupportEmail(final JpaOsfDao jpaOsfDao, final String institutionId) { + final OsfInstitution institution = jpaOsfDao.findOneInstitutionById(institutionId); return institution != null ? institution.getSupportEmail() : null; } + /** + * @param jpaOsfDao the data access object for OSF DB + * @param target the target query param in shibboleth URL + * @param institutionId the institution ID used in shortcut SSO mode + * @return a map of institution name and login URL + */ public static Map getInstitutionLoginUrlMap( final JpaOsfDao jpaOsfDao, final String target, @@ -100,6 +115,12 @@ public static Map getInstitutionLoginUrlMap( return institutionLoginUrlMap; } + /** + * A helper method that sort a map by value instead of key. + * + * @param map the map to sort by value + * @return the sorted map + */ public static > Map sortByValue(final Map map) { final List> list = new LinkedList<>(map.entrySet()); Collections.sort(list, new Comparator>() { diff --git a/src/main/java/io/cos/cas/osf/web/support/OsfCasLoginContext.java b/src/main/java/io/cos/cas/osf/web/support/OsfCasLoginContext.java index e7f476dc..525f762b 100644 --- a/src/main/java/io/cos/cas/osf/web/support/OsfCasLoginContext.java +++ b/src/main/java/io/cos/cas/osf/web/support/OsfCasLoginContext.java @@ -9,10 +9,8 @@ import java.io.Serializable; /** - * This is {@link OsfCasLoginContext}. - * - * Stores OSF-specific information about the current web flow. Extends {@link Serializable} so that it can be put into - * and retrieved from the flow context conveniently. + * This is {@link OsfCasLoginContext}. Stores OSF-specific information about the current web flow. + * Extends {@link Serializable} so that it can be put into and retrieved from the flow context conveniently. * * @author Longze Chen * @since 20.1.0 @@ -28,8 +26,15 @@ public class OsfCasLoginContext implements Serializable { private boolean institutionLogin; + /** + * A verified institution ID. Its being present allows web flow to handle shortcut SSO mode. + */ private String institutionId; + /** + * Indicates whether the institution with {@link this#institutionId} has hidden SSO availability, + * which allows web flow to handle such institutions properly in shortcut SSO mode. + */ private boolean hiddenSsoAvailability; private String institutionSupportEmail; @@ -44,7 +49,6 @@ public class OsfCasLoginContext implements Serializable { /** * The default service URL that uses OSF login endpoint with OSF home as destination. - * * e.g. http(s)://[OSF Domain]/login?next=[encoded version of http(s)://[OSF Domain]/] */ private String defaultServiceUrl;