-
Notifications
You must be signed in to change notification settings - Fork 494
Commit
- Put email addresses throught the same "find single value" logic originally developed in #1608 for multiple first and last names. - Add `@ValidateEmail` to the "email" field on AuthenticatedUser to match BuiltinUser. - Add null check added to EmailValidator to make it testable. - Add `INVALID_EMAIL` and `MISSING_REQUIRED_ATTR` modes for Shib testing in dev. - Remove red warning when TestShib doesn't provide "mail" attribute. - Catch authSvc.createAuthenticatedUser exceptions and handle errors better. - Reformat code (getPrettyFacesHomePageString seems ok).
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -33,9 +33,16 @@ public static boolean isEmailValid(String value, ConstraintValidatorContext cont | |||||
//we'll let someone else decide if it's required | ||||||
return true; | ||||||
} | ||||||
/** | ||||||
* @todo Why are we validating the trimmed value rather than the value | ||||||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
pdurbin
Author
Member
|
au = authSvc.createAuthenticatedUser( |
authSvc.createAuthenticatedUser will happily run applyDisplayInfo (displayInfo contains an email address) and attempt to save without catching ConstraintViolationException. This all has less to do with trimming and more with bean validation, but it's a bit of a problem in my opinion. /cc @michbarsinai @scolapasta
This is why I added a "Couldn't create user" message to show the user in this (unlikely) scenario:
JsfHelper.addErrorMessage("Couldn't create user."); |
This comment has been minimized.
This comment has been minimized.
Sorry, something went wrong.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,7 @@ | |
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
import javax.ejb.EJB; | ||
import javax.ejb.EJBException; | ||
import javax.faces.application.FacesMessage; | ||
import javax.faces.context.ExternalContext; | ||
import javax.faces.context.FacesContext; | ||
|
@@ -217,6 +218,7 @@ public class Shib implements java.io.Serializable { | |
// private boolean debug = false; | ||
private String emailAddress; | ||
private boolean useHeaders; | ||
private final String testShibIdpEntityId = "https://idp.testshib.org/idp/shibboleth"; | ||
|
||
public enum State { | ||
|
||
|
@@ -278,18 +280,36 @@ public void init() { | |
lastName = betterLastName; | ||
} | ||
} | ||
String emailAddressInAssertion = null; | ||
try { | ||
emailAddress = getRequiredValueFromAssertion(emailAttribute); | ||
emailAddressInAssertion = getRequiredValueFromAssertion(emailAttribute); | ||
} catch (Exception ex) { | ||
String testShibIdpEntityId = "https://idp.testshib.org/idp/shibboleth"; | ||
if (shibIdp.equals(testShibIdpEntityId)) { | ||
logger.info("For " + testShibIdpEntityId + " (which as of this writing doesn't provide the " + emailAttribute + " attribute) setting email address to value of eppn: " + shibUserIdentifier); | ||
emailAddress = shibUserIdentifier; | ||
emailAddressInAssertion = shibUserIdentifier; | ||
} else { | ||
// forcing all other IdPs to send us an an email | ||
return; | ||
} | ||
} | ||
|
||
if (!EMailValidator.isEmailValid(emailAddressInAssertion, null)) { | ||
String msg = "The SAML assertion contained an invalid email address: \"" + emailAddressInAssertion + "\"."; | ||
logger.info(msg); | ||
String singleEmailAddress = ShibUtil.findSingleValue(emailAddressInAssertion); | ||
if (EMailValidator.isEmailValid(singleEmailAddress, null)) { | ||
msg = "Multiple email addresses were asserted by the Identity Provider (" + emailAddressInAssertion + " ). These were sorted and the first was chosen: " + singleEmailAddress; | ||
logger.info(msg); | ||
emailAddress = singleEmailAddress; | ||
} else { | ||
msg += " A single valid address could not be found."; | ||
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, identityProviderProblem, msg)); | ||
return; | ||
} | ||
} else { | ||
emailAddress = emailAddressInAssertion; | ||
} | ||
|
||
String usernameAssertion = getValueFromAssertion(usernameAttribute); | ||
internalUserIdentifer = ShibUtil.generateFriendlyLookingUserIdentifer(usernameAssertion, emailAddress); | ||
logger.info("friendly looking identifer (backend will enforce uniqueness):" + internalUserIdentifer); | ||
|
@@ -312,6 +332,7 @@ public void init() { | |
*/ | ||
// String displayName = getDisplayName(displayNameAttribute, firstNameAttribute, lastNameAttribute); | ||
String affiliation = getAffiliation(); | ||
// emailAddress = "willFailBeanValidation"; // for testing createAuthenticatedUser exceptions | ||
displayInfo = new AuthenticatedUserDisplayInfo(firstName, lastName, emailAddress, affiliation, null); | ||
|
||
userPersistentId = shibIdp + persistentUserIdSeparator + shibUserIdentifier; | ||
|
@@ -475,6 +496,8 @@ public enum DevShibAccountType { | |
HARVARD1, | ||
HARVARD2, | ||
TWO_EMAILS, | ||
INVALID_EMAIL, | ||
MISSING_REQUIRED_ATTR, | ||
}; | ||
|
||
private DevShibAccountType getDevShibAccountType() { | ||
|
@@ -538,6 +561,14 @@ private void possiblyMutateRequestInDev() { | |
mutateRequestForDevConstantTwoEmails(); | ||
break; | ||
|
||
case INVALID_EMAIL: | ||
mutateRequestForDevConstantInvalidEmail(); | ||
break; | ||
|
||
case MISSING_REQUIRED_ATTR: | ||
mutateRequestForDevConstantMissingRequiredAttributes(); | ||
break; | ||
|
||
default: | ||
logger.info("Should never reach here"); | ||
break; | ||
|
@@ -555,14 +586,22 @@ private void printHeaders() { | |
public String confirmAndCreateAccount() { | ||
ShibAuthenticationProvider shibAuthProvider = new ShibAuthenticationProvider(); | ||
String lookupStringPerAuthProvider = userPersistentId; | ||
AuthenticatedUser au = authSvc.createAuthenticatedUser( | ||
new UserRecordIdentifier(shibAuthProvider.getId(), lookupStringPerAuthProvider), internalUserIdentifer, displayInfo, true); | ||
AuthenticatedUser au = null; | ||
try { | ||
au = authSvc.createAuthenticatedUser( | ||
new UserRecordIdentifier(shibAuthProvider.getId(), lookupStringPerAuthProvider), internalUserIdentifer, displayInfo, true); | ||
} catch (EJBException ex) { | ||
/** | ||
* @todo Show the ConstraintViolationException, if any. | ||
*/ | ||
logger.info("Couldn't create user " + userPersistentId + " due to exception: " + ex.getCause()); | ||
} | ||
if (au != null) { | ||
logger.info("created user " + au.getIdentifier()); | ||
logInUserAndSetShibAttributes(au); | ||
} else { | ||
logger.info("couldn't create user " + userPersistentId); | ||
JsfHelper.addErrorMessage("Couldn't create user."); | ||
This comment has been minimized.
Sorry, something went wrong.
pdurbin
Author
Member
|
||
} | ||
logInUserAndSetShibAttributes(au); | ||
return getPrettyFacesHomePageString(true); | ||
} | ||
|
||
|
@@ -650,7 +689,13 @@ private String getRequiredValueFromAssertion(String key) throws Exception { | |
if (attributeOrHeader == null) { | ||
String msg = "The SAML assertion for \"" + key + "\" was null. Please contact support."; | ||
logger.info(msg); | ||
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, identityProviderProblem, msg)); | ||
boolean showMessage = true; | ||
if (shibIdp.equals(testShibIdpEntityId) && key.equals(emailAttribute)) { | ||
showMessage = false; | ||
} | ||
if (showMessage) { | ||
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, identityProviderProblem, msg)); | ||
} | ||
throw new Exception(msg); | ||
} | ||
String attributeValue = attributeOrHeader.toString(); | ||
|
@@ -704,16 +749,14 @@ public String getPrettyFacesHomePageString(boolean includeFacetDashRedirect) { | |
} else { | ||
return plainHomepageString + "?faces-redirect=true"; | ||
} | ||
} else if (rootDvAlias != null) { | ||
/** | ||
* @todo Is there a constant for "/dataverse/" anywhere? I guess | ||
* we'll just hard-code it here. | ||
*/ | ||
return "/dataverse/" + rootDvAlias; | ||
} else { | ||
if (rootDvAlias != null) { | ||
/** | ||
* @todo Is there a constant for "/dataverse/" anywhere? I guess | ||
* we'll just hard-code it here. | ||
*/ | ||
return "/dataverse/" + rootDvAlias; | ||
} else { | ||
return plainHomepageString; | ||
} | ||
return plainHomepageString; | ||
} | ||
} | ||
|
||
|
@@ -876,7 +919,7 @@ private void mutateRequestForDevRandom() throws JsonSyntaxException, JsonIOExcep | |
} | ||
|
||
private void mutateRequestForDevConstantTestShib1() { | ||
request.setAttribute(shibIdpAttribute, "https://idp.testshib.org/idp/shibboleth"); | ||
request.setAttribute(shibIdpAttribute, testShibIdpEntityId); | ||
// the TestShib "eppn" looks like an email address | ||
request.setAttribute(uniquePersistentIdentifier, "saml@testshib.org"); | ||
// request.setAttribute(displayNameAttribute, "Sam El"); | ||
|
@@ -916,4 +959,29 @@ private void mutateRequestForDevConstantTwoEmails() { | |
request.setAttribute(usernameAttribute, "eallman"); | ||
} | ||
|
||
private void mutateRequestForDevConstantInvalidEmail() { | ||
request.setAttribute(shibIdpAttribute, "https://fake.example.com/idp/shibboleth"); | ||
request.setAttribute(uniquePersistentIdentifier, "invalidEmail"); | ||
request.setAttribute(firstNameAttribute, "Invalid"); | ||
request.setAttribute(lastNameAttribute, "Email"); | ||
request.setAttribute(emailAttribute, "invalidEmail"); | ||
request.setAttribute(usernameAttribute, "invalidEmail"); | ||
|
||
} | ||
|
||
private void mutateRequestForDevConstantMissingRequiredAttributes() { | ||
request.setAttribute(shibIdpAttribute, "https://fake.example.com/idp/shibboleth"); | ||
/** | ||
* @todo When shibIdpAttribute is set to null why don't we see the error | ||
* in the GUI? | ||
*/ | ||
// request.setAttribute(shibIdpAttribute, null); | ||
request.setAttribute(uniquePersistentIdentifier, "missing"); | ||
request.setAttribute(uniquePersistentIdentifier, null); | ||
request.setAttribute(firstNameAttribute, "Missing"); | ||
request.setAttribute(lastNameAttribute, "Required"); | ||
request.setAttribute(emailAttribute, "missing@mailinator.com"); | ||
request.setAttribute(usernameAttribute, "missing"); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,11 +48,7 @@ public class AuthenticatedUser implements User, Serializable { | |
@Column(nullable = false, unique=true) | ||
private String userIdentifier; | ||
|
||
/** | ||
* @todo Uncomment the ValidateEmail annotation below for consistency with | ||
* the annotation on BuiltinUser. | ||
*/ | ||
// @ValidateEmail(message = "Please enter a valid email address.") | ||
This comment has been minimized.
Sorry, something went wrong. |
||
@ValidateEmail(message = "Please enter a valid email address.") | ||
This comment has been minimized.
Sorry, something went wrong.
pdurbin
Author
Member
|
||
@NotNull | ||
@Column(nullable = false, unique=true) | ||
private String email; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package edu.harvard.iq.dataverse; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import org.junit.Test; | ||
|
||
public class EMailValidatorTest { | ||
|
||
@Test | ||
public void testIsEmailValid() { | ||
assertEquals(true, EMailValidator.isEmailValid("pete@mailinator.com", null)); | ||
assertEquals(false, EMailValidator.isEmailValid("pete1@mailinator.com;pete2@mailinator.com", null)); | ||
assertEquals(false, EMailValidator.isEmailValid("", null)); | ||
/** | ||
* @todo How can null as an email address be valid?!? | ||
*/ | ||
assertEquals(true, EMailValidator.isEmailValid(null, null)); | ||
} | ||
|
||
} |
This question can be answered by searching the code.
Following from
dataverse/src/main/java/edu/harvard/iq/dataverse/Shib.java
Line 285 in ffd1d6e
it seems the untrimmed email is persisted.