|
98 | 98 | import java.io.IOException; |
99 | 99 | import java.io.StringWriter; |
100 | 100 | import java.util.Collections; |
| 101 | +import java.util.Arrays; |
101 | 102 | import java.util.Date; |
| 103 | +import java.util.HashSet; |
102 | 104 | import java.util.List; |
103 | 105 | import java.util.Map; |
| 106 | +import java.util.Set; |
104 | 107 | import java.util.concurrent.TimeUnit; |
105 | 108 |
|
106 | 109 | /** |
@@ -166,6 +169,8 @@ public class OsfPrincipalFromNonInteractiveCredentialsAction extends AbstractNon |
166 | 169 |
|
167 | 170 | private static final String ATTRIBUTE_PREFIX = "auth-"; |
168 | 171 |
|
| 172 | + private static final String MULTIPLE_ATTRIBUTE_DELIMITER = ";"; |
| 173 | + |
169 | 174 | private static final String SHIBBOLETH_SESSION_HEADER = ATTRIBUTE_PREFIX + "shib-session-id"; |
170 | 175 |
|
171 | 176 | private static final String SHIBBOLETH_COOKIE_PREFIX = "_shibsession_"; |
@@ -720,7 +725,7 @@ private OsfApiInstitutionAuthenticationResult notifyOsfApiOfInstnAuthnSuccess( |
720 | 725 | // CAS expects OSF API to return HTTP 204 OK with no content if authentication succeeds |
721 | 726 | if (statusCode == HttpStatus.SC_NO_CONTENT) { |
722 | 727 | LOGGER.info("[OSF API] Success - API request succeeded: {}, attempt={}, status={}", ssoUser, retry, statusCode); |
723 | | - return new OsfApiInstitutionAuthenticationResult(institutionId, ssoEmail, ssoIdentity); |
| 728 | + return new OsfApiInstitutionAuthenticationResult(institutionId, deduplicateSsoEmail(ssoEmail, ssoUser), ssoIdentity); |
724 | 729 | } |
725 | 730 | if (OSF_API_RETRY_STATUS.contains(statusCode)) { |
726 | 731 | LOGGER.error("[OSF API] Failure - Server Error: {}, attempt={}, status={}", ssoUser, retry, statusCode); |
@@ -869,4 +874,32 @@ private void setSsoErrorContext( |
869 | 874 | ); |
870 | 875 | context.getFlowScope().put(PARAMETER_SSO_ERROR_CONTEXT, ssoErrorContext); |
871 | 876 | } |
| 877 | + |
| 878 | + /** |
| 879 | + * Attempt to deduplicate the {@code ssoEmail} attribute. This method is only called after OSF API has already |
| 880 | + * successfully deduplicated the attribute and thus should throw {@link InstitutionSsoFailedException} if failed. |
| 881 | + * |
| 882 | + * @param ssoEmail the SSO email to deduplicate |
| 883 | + * @param ssoUser the SSO user information |
| 884 | + * @return deduplicated SSO email on success |
| 885 | + * @throws InstitutionSsoFailedException if deduplication failed |
| 886 | + */ |
| 887 | + private String deduplicateSsoEmail( |
| 888 | + final String ssoEmail, |
| 889 | + final String ssoUser |
| 890 | + ) throws InstitutionSsoFailedException { |
| 891 | + if (StringUtils.isBlank(ssoEmail)) { |
| 892 | + LOGGER.error("[OSF CAS] Critical Error: SSO email should not be blank after OSF API success: [{}]", ssoUser); |
| 893 | + throw new InstitutionSsoFailedException("SSO email should not be blank"); |
| 894 | + } |
| 895 | + if (!ssoEmail.contains(MULTIPLE_ATTRIBUTE_DELIMITER)) { |
| 896 | + return ssoEmail; |
| 897 | + } |
| 898 | + Set<String> ssoEmailSet = new HashSet<>(Arrays.asList(ssoEmail.split(MULTIPLE_ATTRIBUTE_DELIMITER))); |
| 899 | + if (ssoEmailSet.size() != 1) { |
| 900 | + LOGGER.error("[OSF CAS] Critical Error: SSO email should not fail deduplication after OSF API success: [{}]", ssoUser); |
| 901 | + throw new InstitutionSsoFailedException("SSO email should not fail deduplication"); |
| 902 | + } |
| 903 | + return ssoEmailSet.iterator().next(); |
| 904 | + } |
872 | 905 | } |
0 commit comments