diff --git a/CHANGELOG.md b/CHANGELOG.md
index f2189b6c..5ff612e5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
We follow the CalVer (https://calver.org/) versioning scheme: YY.MINOR.MICRO.
+26.1.0 (2026-05-07)
+===================
+
+* OSF4I In-progress SSO Project - CAS Piece
25.1.1 (10-11-2025)
===================
diff --git a/README.md b/README.md
index 56bb2da2..7ed04db4 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@ OSF CAS is the centralized authentication and authorization service for the [OSF
# Implementations
-The implementation of OSF CAS is based on [Apereo CAS 6.2.8](https://github.com/apereo/cas/tree/v6.2.8) via [CAS Overlay Template 6.2.x](https://github.com/apereo/cas-overlay-template/tree/6.2). Refer to [CAS Documentation 6.2.x](https://apereo.github.io/cas/6.2.x/) for more details.
+The implementation of OSF CAS is based on [Apereo CAS 6.2.8](https://github.com/apereo/cas/tree/v6.2.8) via [CAS Overlay Template 6.2.x](https://github.com/apereo/cas-overlay-template/tree/6.2). Refer to [CAS Documentation 6.2.x](https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/) for more details.
## Legacy Implementations
@@ -40,6 +40,33 @@ A legacy version can be found at [CAS Overlay](https://github.com/CenterForOpenS
- PostgreSQL `9.6`
- JDK `11`
+
+# Local Development
+
+For local development, replace the default Dockerfile with Dockerfile-local.
+This is required to ensure proper loading and usage of cas-local.properties.
+```bash
+cp Dockerfile-local Dockerfile
+```
+
+# Special Instructions for Apple Silicon (M1, M2, M3) and Other ARM64 Architectures
+
+If you are running Docker on ARM64 architecture (Apple Silicon or similar), you must explicitly set the platform to linux/amd64 when using OpenJDK 11 images.
+Without this, the CAS container may fail to build or run correctly.
+
+Update the Dockerfile as follows:
+```dockerfile
+# Dockerfile
+
+FROM --platform=linux/amd64 adoptopenjdk/openjdk11:alpine-slim AS overlay
+...
+...
+
+FROM --platform=linux/amd64 adoptopenjdk/openjdk11:alpine-jre AS cas
+...
+```
+This forces Docker to use an amd64 image via emulation and ensures compatibility with CAS and OpenJDK 11 on ARM-based machines.
+
# Configure, Build and Run OSF CAS
It is recommended to use the provided scripts to [build](https://github.com/CenterForOpenScience/osf-cas/blob/develop/docker-build.sh) and [run](https://github.com/CenterForOpenScience/osf-cas/blob/develop/docker-run.sh) CAS. Refer to Apereo [README.md](https://github.com/apereo/cas-overlay-template/tree/6.2#cas-overlay-template-) for more options.
@@ -68,27 +95,20 @@ cas.authn.osf-postgres.jpa.dialect=io.cos.cas.osf.hibernate.dialect.OsfPostgresD
## CAS DB
-The implementation of OSF CAS uses the [JPA Ticket Registry](https://apereo.github.io/cas/6.2.x/ticketing/Configuring-Ticketing-Components.html#ticket-registry) for durable ticket storage and thus requires a relational database. Set up a `PostgreSQL@9.6` server and review [JPA Ticket Registry settings](https://github.com/CenterForOpenScience/osf-cas/blob/d0a03b51c9b1ce7795a210223c1ce38d5b2742de/etc/cas/config/cas.properties#L127-L173). In most cases, only [Database connections](https://github.com/CenterForOpenScience/osf-cas/blob/d0a03b51c9b1ce7795a210223c1ce38d5b2742de/etc/cas/config/cas.properties#L139-L143) need to be updated. Other JDBC and JPA settings can be adjusted if necessary.
+The implementation of OSF CAS uses the [JPA Ticket Registry](https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/ticketing/Configuring-Ticketing-Components.md#ticket-registry) for durable ticket storage and thus requires a relational database. Set up a `PostgreSQL@9.6` server and review [JPA Ticket Registry settings](https://github.com/CenterForOpenScience/osf-cas/blob/d0a03b51c9b1ce7795a210223c1ce38d5b2742de/etc/cas/config/cas.properties#L127-L173). In most cases, only [Database connections](https://github.com/CenterForOpenScience/osf-cas/blob/d0a03b51c9b1ce7795a210223c1ce38d5b2742de/etc/cas/config/cas.properties#L139-L143) need to be updated. Other JDBC and JPA settings can be adjusted if necessary.
-Here is an example for local development. Use `192.168.168.167` to access host outside the docker container. Use the port `54321` since the default `5432` one has been used by OSF DB. Update `pg_hba.conf` to grant proper access permission depending on the setup.
+Here is an example for local development. Use `192.168.168.167` to access host outside the docker container.
```yaml
# In `cas.properties` or `cas-local.properties`
-cas.ticket.registry.jpa.user=longzechen
+cas.ticket.registry.jpa.user=postgres
cas.ticket.registry.jpa.password=
cas.ticket.registry.jpa.driver-class=org.postgresql.Driver
-cas.ticket.registry.jpa.url=jdbc:postgresql://192.168.168.167:54321/osf-cas?targetServerType=master
+cas.ticket.registry.jpa.url=jdbc:postgresql://192.168.168.167:5432/osf-cas?targetServerType=master
cas.ticket.registry.jpa.dialect=org.hibernate.dialect.PostgreSQL95Dialect
```
-```yaml
-# In `pg_hba.conf`
-
-# TYPE DATABASE USER ADDRESS METHOD
-host osf-cas longzechen 192.168.168.167/24 trust
-```
-
## Signing and Encryption Keys
### CAS Server
diff --git a/etc/cas/config/attribute-map-prod.xml b/etc/cas/config/attribute-map-prod.xml
index 501bcaa6..26f2ea3b 100644
--- a/etc/cas/config/attribute-map-prod.xml
+++ b/etc/cas/config/attribute-map-prod.xml
@@ -201,6 +201,9 @@
+
+
+
diff --git a/etc/cas/config/cas.properties b/etc/cas/config/cas.properties
index 1c8bad4f..7296e826 100644
--- a/etc/cas/config/cas.properties
+++ b/etc/cas/config/cas.properties
@@ -25,8 +25,8 @@ cas.server.dev-mode.allow-force-http-error=${ALLOW_FORCE_HTTP_ERROR:false}
########################################################################################################################
# Throttling
-# Configuration guide: https://apereo.github.io/cas/6.2.x/installation/Configuring-Authentication-Throttling.html
-# Properties: https://apereo.github.io/cas/6.2.x/configuration/Configuration-Properties.html#authentication-throttling
+# Configuration guide: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/installation/Configuring-Authentication-Throttling.md
+# Properties: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/configuration/Configuration-Properties.md#authentication-throttling
########################################################################################################################
#
# Authentication Failure Throttling
@@ -40,7 +40,7 @@ cas.authn.throttle.failure.range-seconds=1
########################################################################################################################
# CAS Monitoring & Statistics Endpoints
-# See: https://apereo.github.io/cas/6.2.x/monitoring/Monitoring-Statistics.html
+# See: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/monitoring/Monitoring-Statistics.md
########################################################################################################################
management.endpoints.web.exposure.include=health
management.endpoint.health.enabled=true
@@ -72,14 +72,14 @@ cas.authn.accept.users=
########################################################################################################################
# JSON Service Registry
-# See: https://apereo.github.io/cas/6.2.x/services/JSON-Service-Management.html
+# See: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/services/JSON-Service-Management.md
########################################################################################################################
cas.serviceRegistry.json.location=file:/etc/cas/services
########################################################################################################################
########################################################################################################################
# CAS Logout and Single Logout (SLO)
-# https://apereo.github.io/cas/6.2.x/installation/Logout-Single-Signout.html
+# https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/installation/Logout-Single-Signout.md
########################################################################################################################
# CAS Logout
#
@@ -130,7 +130,7 @@ cas.authn.osf-api.instn-authn-xsl-location=file:/etc/cas/institutions-auth.xsl
########################################################################################################################
# OSF PostgreSQL Authentication
-# See: https://apereo.github.io/cas/6.2.x/installation/Configuring-Custom-Authentication.html
+# See: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/installation/Configuring-Custom-Authentication.md
########################################################################################################################
# Authentication settings
#
@@ -148,7 +148,7 @@ cas.authn.osf-postgres.jpa.dialect=${OSF_DB_HIBERNATE_DIALECT:io.cos.cas.osf.hib
########################################################################################################################
# JPA Ticket Registry
-# See: https://apereo.github.io/cas/6.2.x/ticketing/JPA-Ticket-Registry.html
+# See: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/ticketing/JPA-Ticket-Registry.md
########################################################################################################################
# Global JDBC Settings
#
@@ -196,16 +196,16 @@ cas.ticket.registry.jpa.jpa-locking-timeout=PT1H
########################################################################################################################
# Signing and Encryption
-# See: https://apereo.github.io/cas/6.2.x/configuration/Configuration-Properties-Common.html#signing--encryption
+# See: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/configuration/Configuration-Properties-Common.md#signing--encryption
########################################################################################################################
# Spring Client Session
-# See: https://apereo.github.io/cas/6.2.x/configuration/Configuration-Properties.html#spring-webflow-client-side-session
+# See: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/configuration/Configuration-Properties.md#spring-webflow-client-side-session
#
cas.webflow.crypto.signing.key=${WEB_FLOW_SIGNING_KEY}
cas.webflow.crypto.encryption.key=${WEB_FLOW_ENCRYPTION_KEY}
#
# Ticket Granting Cookie (TGC)
-# See: https://apereo.github.io/cas/6.2.x/configuration/Configuration-Properties.html#signing--encryption-4
+# See: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/configuration/Configuration-Properties.md#signing--encryption-4
#
cas.tgc.crypto.signing.key=${TGC_SIGNING_KEY}
cas.tgc.crypto.encryption.key=${TGC_ENCRYPTION_KEY}
@@ -213,7 +213,7 @@ cas.tgc.crypto.encryption.key=${TGC_ENCRYPTION_KEY}
########################################################################################################################
# Long-term Authentication: Ticket Granting Cookie (TGC) and Ticket Granting Ticket (TGT)
-# See https://apereo.github.io/cas/6.2.x/installation/Configuring-LongTerm-Authentication.html
+# See https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/installation/Configuring-LongTerm-Authentication.md
########################################################################################################################
# General Cookie Setting for Ticket Granting Cookie
#
@@ -242,7 +242,7 @@ cas.ticket.tgt.remember-me.time-to-kill-in-seconds=7776000
########################################################################################################################
# Pac4j Delegated Authentication
-# https://apereo.github.io/cas/6.2.x/integration/Delegate-Authentication.html
+# https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/integration/Delegate-Authentication.md
########################################################################################################################
# General settings
#
@@ -275,8 +275,8 @@ cas.authn.pac4j.cas[0].callback-url-type=QUERY_PARAMETER
########################################################################################################################
# OAuth 2.0 Server
-# Configuration guide: https://apereo.github.io/cas/6.2.x/installation/OAuth-OpenId-Authentication.html
-# Properties: https://apereo.github.io/cas/6.2.x/configuration/Configuration-Properties.html#oauth2
+# Configuration guide: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/installation/OAuth-OpenId-Authentication.md
+# Properties: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/configuration/Configuration-Properties.md#oauth2
########################################################################################################################
# Authorization Code
#
diff --git a/etc/cas/config/instn-authn-prod.xsl b/etc/cas/config/instn-authn-prod.xsl
index f1a2140d..36bd1421 100644
--- a/etc/cas/config/instn-authn-prod.xsl
+++ b/etc/cas/config/instn-authn-prod.xsl
@@ -250,7 +250,7 @@
-
+
harvard
@@ -457,6 +457,18 @@
+
+
+ tamu
+
+
+
+
+
+
+
+
+
tu
@@ -895,6 +907,20 @@
false
+
+
+ wsu
+
+
+
+
+
+
+
+
+ false
+
+
wustl
diff --git a/etc/cas/config/instn-authn-test.xsl b/etc/cas/config/instn-authn-test.xsl
index bd556e22..762467e5 100644
--- a/etc/cas/config/instn-authn-test.xsl
+++ b/etc/cas/config/instn-authn-test.xsl
@@ -309,6 +309,18 @@
+
+
+ tamu
+
+
+
+
+
+
+
+
+
ua
diff --git a/etc/cas/config/local/cas-local.properties b/etc/cas/config/local/cas-local.properties
index 6626573c..9347da1c 100644
--- a/etc/cas/config/local/cas-local.properties
+++ b/etc/cas/config/local/cas-local.properties
@@ -30,8 +30,8 @@ cas.server.dev-mode.allow-force-http-error=true
########################################################################################################################
# Throttling
-# Configuration guide: https://apereo.github.io/cas/6.2.x/installation/Configuring-Authentication-Throttling.html
-# Properties: https://apereo.github.io/cas/6.2.x/configuration/Configuration-Properties.html#authentication-throttling
+# Configuration guide: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/installation/Configuring-Authentication-Throttling.md
+# Properties: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/configuration/Configuration-Properties.md#authentication-throttling
########################################################################################################################
#
# Authentication Failure Throttling
@@ -45,7 +45,7 @@ cas.authn.throttle.failure.range-seconds=1
########################################################################################################################
# CAS Monitoring & Statistics Endpoints
-# See: https://apereo.github.io/cas/6.2.x/monitoring/Monitoring-Statistics.html
+# See: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/monitoring/Monitoring-Statistics.md
########################################################################################################################
management.endpoints.web.exposure.include=health
management.endpoint.health.enabled=true
@@ -78,14 +78,14 @@ cas.authn.accept.users=
########################################################################################################################
# JSON Service Registry
-# See: https://apereo.github.io/cas/6.2.x/services/JSON-Service-Management.html
+# See: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/services/JSON-Service-Management.md
########################################################################################################################
cas.serviceRegistry.json.location=file:/etc/cas/services
########################################################################################################################
########################################################################################################################
# CAS Logout and Single Logout (SLO)
-# https://apereo.github.io/cas/6.2.x/installation/Logout-Single-Signout.html
+# https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/installation/Logout-Single-Signout.md
########################################################################################################################
# CAS Logout
#
@@ -136,7 +136,7 @@ cas.authn.osf-api.instn-authn-xsl-location=file:/etc/cas/config/instn-authn.xsl
########################################################################################################################
# OSF PostgreSQL Authentication
-# See: https://apereo.github.io/cas/6.2.x/installation/Configuring-Custom-Authentication.html
+# See: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/installation/Configuring-Custom-Authentication.md
########################################################################################################################
# Authentication settings
#
@@ -154,7 +154,7 @@ cas.authn.osf-postgres.jpa.dialect=io.cos.cas.osf.hibernate.dialect.OsfPostgresD
########################################################################################################################
# JPA Ticket Registry
-# See: https://apereo.github.io/cas/6.2.x/ticketing/JPA-Ticket-Registry.html
+# See: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/ticketing/JPA-Ticket-Registry.md
########################################################################################################################
# Global JDBC Settings
#
@@ -204,16 +204,16 @@ cas.ticket.registry.jpa.jpa-locking-timeout=PT1H
########################################################################################################################
# Signing and Encryption
-# See: https://apereo.github.io/cas/6.2.x/configuration/Configuration-Properties-Common.html#signing--encryption
+# See: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/configuration/Configuration-Properties-Common.md#signing--encryption
########################################################################################################################
# Spring Client Session
-# See: https://apereo.github.io/cas/6.2.x/configuration/Configuration-Properties.html#spring-webflow-client-side-session
+# See: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/configuration/Configuration-Properties.md#spring-webflow-client-side-session
#
cas.webflow.crypto.signing.key=changeme
cas.webflow.crypto.encryption.key=changeme
#
# Ticket Granting Cookie (TGC)
-# See: https://apereo.github.io/cas/6.2.x/configuration/Configuration-Properties.html#signing--encryption-4
+# See: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/configuration/Configuration-Properties.md#signing--encryption-4
#
cas.tgc.crypto.signing.key=changeme
cas.tgc.crypto.encryption.key=changeme
@@ -221,7 +221,7 @@ cas.tgc.crypto.encryption.key=changeme
########################################################################################################################
# Long-term Authentication: Ticket Granting Cookie (TGC) and Ticket Granting Ticket (TGT)
-# See https://apereo.github.io/cas/6.2.x/installation/Configuring-LongTerm-Authentication.html
+# See https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/installation/Configuring-LongTerm-Authentication.md
########################################################################################################################
# General Cookie Setting for Ticket Granting Cookie
#
@@ -248,7 +248,7 @@ cas.ticket.tgt.remember-me.time-to-kill-in-seconds=7200
########################################################################################################################
# Pac4j Delegated Authentication
-# https://apereo.github.io/cas/6.2.x/integration/Delegate-Authentication.html
+# https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/integration/Delegate-Authentication.md
########################################################################################################################
# General settings
#
@@ -287,8 +287,8 @@ cas.authn.pac4j.cas[1].callback-url-type=QUERY_PARAMETER
########################################################################################################################
# OAuth 2.0 Server
-# Configuration guide: https://apereo.github.io/cas/6.2.x/installation/OAuth-OpenId-Authentication.html
-# Properties: https://apereo.github.io/cas/6.2.x/configuration/Configuration-Properties.html#oauth2
+# Configuration guide: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/installation/OAuth-OpenId-Authentication.md
+# Properties: https://github.com/apereo/cas/blob/6.2.x/docs/cas-server-documentation/configuration/Configuration-Properties.md#oauth2
########################################################################################################################
# Authorization Code
#
diff --git a/etc/cas/config/shibboleth2-prod.xml b/etc/cas/config/shibboleth2-prod.xml
index ffcd05cd..55f8e789 100644
--- a/etc/cas/config/shibboleth2-prod.xml
+++ b/etc/cas/config/shibboleth2-prod.xml
@@ -98,6 +98,12 @@
backingFilePath="vua-prod-idp-metadata.xml"
reloadInterval="180000" />
+
+
+
@@ -121,6 +127,7 @@
+
diff --git a/etc/cas/config/shibboleth2-test.xml b/etc/cas/config/shibboleth2-test.xml
index 7da62567..a88de802 100644
--- a/etc/cas/config/shibboleth2-test.xml
+++ b/etc/cas/config/shibboleth2-test.xml
@@ -118,6 +118,7 @@
+
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 89b28147..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
@@ -14,7 +14,7 @@
import java.util.Map;
/**
- * This is {@link OsfInstitutionUtils}.
+ * This is {@link OsfInstitutionUtils}, which provides helper methods supporting the institution SSO login flow.
*
* @author Longze Chen
* @since 21.0.0
@@ -24,34 +24,79 @@ 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);
- return institution != null && institution.getDelegationProtocol() != null;
+ /**
+ * @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,
- final String id
+ final String institutionId
) {
List institutionList = new LinkedList<>();
- if (id == null || id.isEmpty()) {
+ boolean isShortcutSso = false;
+ if (institutionId == null || institutionId.isEmpty()) {
institutionList = jpaOsfDao.findAllInstitutions();
} else {
- final OsfInstitution institution = jpaOsfDao.findOneInstitutionById(id);
+ final OsfInstitution institution = jpaOsfDao.findOneInstitutionById(institutionId);
if (institution != null) {
+ // Must be a valid institution to trigger the shortcut SSO mode
institutionList.add(institution);
+ isShortcutSso = true;
} else {
institutionList = jpaOsfDao.findAllInstitutions();
}
}
final Map institutionLoginUrlMap = new HashMap<>();
for (final OsfInstitution institution: institutionList) {
+ final SsoAvailability ssoAvailability = institution.getSsoAvailability();
+ if (ssoAvailability == null) {
+ // Catch a rare exception case where OSF DB has changed the choices of the field
+ // `sso_availability` in table `osf_institution` without syncing with CAS.
+ LOGGER.error(
+ "Skip instn with invalid SSO avail: [instnId={}]",
+ institution.getInstitutionId()
+ );
+ continue;
+ }
+ if (isShortcutSso && ssoAvailability.isHidden()) {
+ // Show institutions of hidden SSO Availability in shortcut mode
+ LOGGER.debug(
+ "Show instn with hidden SSO avail in shortcut mode: [instnId={}, avail={}]",
+ institution.getInstitutionId(),
+ ssoAvailability.getId()
+ );
+ } else if (!ssoAvailability.isPublic()) {
+ // Hide institutions of non-public SSO Availability
+ LOGGER.debug(
+ "Skip instn with non-public SSO avail: [instnId={}, avail={}]",
+ institution.getInstitutionId(),
+ ssoAvailability.getId()
+ );
+ continue;
+ }
final DelegationProtocol delegationProtocol = institution.getDelegationProtocol();
if (DelegationProtocol.SAML_SHIB.equals(delegationProtocol)) {
institutionLoginUrlMap.put(
@@ -70,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/authentication/support/SsoAvailability.java b/src/main/java/io/cos/cas/osf/authentication/support/SsoAvailability.java
new file mode 100644
index 00000000..fbcfd982
--- /dev/null
+++ b/src/main/java/io/cos/cas/osf/authentication/support/SsoAvailability.java
@@ -0,0 +1,64 @@
+package io.cos.cas.osf.authentication.support;
+
+/**
+ * This is {@link SsoAvailability}, which is used in {@link io.cos.cas.osf.model.OsfInstitution}
+ * to map to the types/choices of its counterpart in the OSF model.
+ *
+ * @author Longze Chen
+ * @since 26.1.0
+ */
+public enum SsoAvailability {
+
+ /**
+ * The institution is active, has a delegation protocol, and its SSO setup has been verified.
+ * */
+ PUBLIC("Public"),
+ /**
+ * The institution is either: 1) inactive and has a delegation protocol,
+ * or 2) active, has a delegation protocol but its SSO setup is in-progress.
+ */
+ HIDDEN("Hidden"),
+ /**
+ * The institution does not have a delegation protocol (i.e. not eligible for SSO).
+ */
+ UNAVAILABLE("Unavailable");
+
+ private final String id;
+
+ SsoAvailability(final String id) {
+ this.id = id;
+ }
+
+ public static SsoAvailability getType(final String id) throws IllegalArgumentException {
+ if (id == null) {
+ return null;
+ }
+ for (final SsoAvailability type : SsoAvailability.values()) {
+ if (id.equals(type.getId())) {
+ return type;
+ }
+ }
+ throw new IllegalArgumentException("No matching type for id " + id);
+ }
+
+ /**
+ * @return whether the enum type is {@link SsoAvailability#PUBLIC}.
+ */
+ public boolean isPublic () {
+ return SsoAvailability.PUBLIC.equals(this);
+ }
+
+ /**
+ * @return whether the enum type is {@link SsoAvailability#HIDDEN}.
+ */
+ public boolean isHidden () {
+ return SsoAvailability.HIDDEN.equals(this);
+ }
+
+ /**
+ * @return the enum type string
+ */
+ public final String getId() {
+ return id;
+ }
+}
diff --git a/src/main/java/io/cos/cas/osf/model/OsfInstitution.java b/src/main/java/io/cos/cas/osf/model/OsfInstitution.java
index 15ad821d..bf44ef95 100644
--- a/src/main/java/io/cos/cas/osf/model/OsfInstitution.java
+++ b/src/main/java/io/cos/cas/osf/model/OsfInstitution.java
@@ -1,6 +1,7 @@
package io.cos.cas.osf.model;
import io.cos.cas.osf.authentication.support.DelegationProtocol;
+import io.cos.cas.osf.authentication.support.SsoAvailability;
import lombok.Getter;
import lombok.NoArgsConstructor;
@@ -14,7 +15,8 @@
import java.util.Date;
/**
- * This is {@link OsfInstitution}.
+ * This is {@link OsfInstitution}. It maps to a subset of columns in the OSF DB table {@code osf_instittuion}.
+ * This subset is required to support institution SSO for CAS and OSF.
*
* @author Longze Chen
* @since 21.0.0
@@ -40,9 +42,18 @@ public class OsfInstitution extends AbstractOsfModel {
@Column(name = "logout_url")
private String logoutUrl;
+ /**
+ * Maps to column {@code delegation_protocol} of table {@code osf_instittuion} in OSF database.
+ */
@Column(name = "delegation_protocol")
private String delegationProtocol;
+ /**
+ * Maps to column {@code sso_availability} of table {@code osf_instittuion} in OSF database.
+ */
+ @Column(name = "sso_availability")
+ private String ssoAvailability;
+
@Column(name = "is_deleted")
private Boolean deleted;
@@ -53,6 +64,9 @@ public class OsfInstitution extends AbstractOsfModel {
@Column(name = "support_email", nullable = false)
private String supportEmail;
+ /**
+ * @return the institution's delegation protocol.
+ */
public DelegationProtocol getDelegationProtocol() {
try {
return DelegationProtocol.getType(delegationProtocol);
@@ -60,4 +74,15 @@ public DelegationProtocol getDelegationProtocol() {
return null;
}
}
+
+ /**
+ * @return the institution's SSO Availability.
+ */
+ public SsoAvailability getSsoAvailability() {
+ try {
+ return SsoAvailability.getType(ssoAvailability);
+ } catch (final IllegalArgumentException e) {
+ return 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..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";
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..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
@@ -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,16 +77,20 @@ 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 {
- final String institutionSupportEmail = OsfInstitutionUtils.getInstitutionSupportEmail(jpaOsfDao, institutionId);
+ 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
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..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,17 @@ 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;
private boolean unsupportedInstitutionLogin;
@@ -42,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;
diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties
index 25aa8bb5..a6a68470 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=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
@@ -672,7 +673,6 @@ screen.error.page.loginagain=Log in again
screen.authnerror.title=Login Error
screen.authnerror.tips=Oops! Something went wrong ...
screen.authnerror.tips.devmode=Developer mode only !!!
-screen.authnerror.button.resendosfconfirmation=Resend confirmation email
screen.authnerror.button.backtoosf=Exit login
screen.authnerror.button.logout=Log out
screen.blocked.message=You are being throttled for attempting to login too frequently in a short amount of time. \
@@ -688,7 +688,8 @@ screen.accountnotconfirmedidp.message=The OSF account associated with the email
"mailto:support@osf.io">OSF Support.
screen.accountnotconfirmedosf.heading=Account not confirmed
screen.accountnotconfirmedosf.message=The OSF account associated with the email has been registered but not confirmed. \
- Please check your email (and spam folder) or click the button below to resend your confirmation email.
+ Please check your email (and spam folder) or contact OSF \
+ Support.
screen.servererror.title=Server Error
screen.unavailable.heading=CAS unavailable
screen.unavailable.message=Your request cannot be completed at this time. Please return to OSF and try again later. If \
diff --git a/src/main/resources/templates/casAccountNotConfirmedIdPView.html b/src/main/resources/templates/casAccountNotConfirmedIdPView.html
index c9156b8b..a021a176 100644
--- a/src/main/resources/templates/casAccountNotConfirmedIdPView.html
+++ b/src/main/resources/templates/casAccountNotConfirmedIdPView.html
@@ -18,11 +18,6 @@
-
diff --git a/src/main/resources/templates/casAccountNotConfirmedOsfView.html b/src/main/resources/templates/casAccountNotConfirmedOsfView.html
index f9564799..9beeb0c2 100644
--- a/src/main/resources/templates/casAccountNotConfirmedOsfView.html
+++ b/src/main/resources/templates/casAccountNotConfirmedOsfView.html
@@ -18,11 +18,6 @@
-
diff --git a/src/main/resources/templates/casInstitutionLoginView.html b/src/main/resources/templates/casInstitutionLoginView.html
index bdf9f08e..f482072b 100644
--- a/src/main/resources/templates/casInstitutionLoginView.html
+++ b/src/main/resources/templates/casInstitutionLoginView.html
@@ -31,6 +31,10 @@
+
+