From 90144437809a8e2024431f4469b6c807b62de6d7 Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Fri, 13 May 2016 16:23:15 -0400 Subject: [PATCH 1/2] Shib: code cleanup of convertShibToBuiltIn #2915 --- .../edu/harvard/iq/dataverse/api/Admin.java | 86 +++---------------- .../AuthenticationServiceBean.java | 65 +++++++++++--- 2 files changed, 64 insertions(+), 87 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Admin.java b/src/main/java/edu/harvard/iq/dataverse/api/Admin.java index 3bff0f09c76..fd8618b23f8 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Admin.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Admin.java @@ -6,14 +6,12 @@ import static edu.harvard.iq.dataverse.api.AbstractApiBean.errorResponse; import edu.harvard.iq.dataverse.api.dto.RoleDTO; import edu.harvard.iq.dataverse.authorization.AuthenticatedUserDisplayInfo; -import edu.harvard.iq.dataverse.authorization.AuthenticatedUserLookup; import edu.harvard.iq.dataverse.authorization.AuthenticationProvider; import edu.harvard.iq.dataverse.authorization.UserIdentifier; import edu.harvard.iq.dataverse.authorization.exceptions.AuthenticationProviderFactoryNotFoundException; import edu.harvard.iq.dataverse.authorization.exceptions.AuthorizationSetupException; import edu.harvard.iq.dataverse.authorization.providers.AuthenticationProviderFactory; import edu.harvard.iq.dataverse.authorization.providers.AuthenticationProviderRow; -import edu.harvard.iq.dataverse.authorization.providers.builtin.BuiltinAuthenticationProvider; import edu.harvard.iq.dataverse.authorization.providers.builtin.BuiltinUser; import edu.harvard.iq.dataverse.authorization.providers.builtin.BuiltinUserServiceBean; import edu.harvard.iq.dataverse.authorization.providers.shib.ShibAuthenticationProvider; @@ -35,7 +33,6 @@ import static edu.harvard.iq.dataverse.util.json.NullSafeJsonBuilder.jsonObjectBuilder; import static edu.harvard.iq.dataverse.util.json.JsonPrinter.*; -import java.sql.SQLException; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -266,9 +263,6 @@ public Response listAuthenticatedUsers() { } /** - * @todo Refactor more of this business logic into ShibServiceBean in case - * we want to build a superuser GUI around this some day. - * * curl -X PUT -d "shib@mailinator.com" * http://localhost:8080/api/admin/authenticatedUsers/id/11/convertShibToBuiltIn */ @@ -283,78 +277,20 @@ public Response convertShibUserToBuiltin(@PathParam("id") Long id, String newEma } catch (WrappedResponse ex) { return errorResponse(Response.Status.FORBIDDEN, "Superusers only."); } - AuthenticatedUser userToConvert = authSvc.findByID(id); - if (userToConvert == null) { - return errorResponse(Response.Status.BAD_REQUEST, "User id " + id + " not found."); - } - AuthenticatedUserLookup lookup = userToConvert.getAuthenticatedUserLookup(); - if (lookup == null) { - return errorResponse(Response.Status.BAD_REQUEST, "User id " + id + " does not have an 'authenticateduserlookup' row"); - } - String providerId = lookup.getAuthenticationProviderId(); - if (providerId == null) { - return errorResponse(Response.Status.BAD_REQUEST, "User id " + id + " provider id is null."); - } - String shibProviderId = ShibAuthenticationProvider.PROVIDER_ID; - if (!providerId.equals(shibProviderId)) { - return errorResponse(Response.Status.BAD_REQUEST, "User id " + id + " cannot be converted because current provider id is '" + providerId + "' rather than '" + shibProviderId + "'."); - } - BuiltinUser builtinUser = null; try { - /** - * @todo Refactor more of the logic and error checking into this - * convertShibToBuiltIn method. - */ - builtinUser = authSvc.convertShibToBuiltIn(userToConvert, newEmailAddress); - } catch (Throwable ex) { - while (ex.getCause() != null) { - ex = ex.getCause(); - } - if (ex instanceof ConstraintViolationException) { - ConstraintViolationException constraintViolationException = (ConstraintViolationException) ex; - StringBuilder userMsg = new StringBuilder(); - StringBuilder logMsg = new StringBuilder(); - logMsg.append("User id " + id + " cannot be converted from Shibboleth to BuiltIn. "); - for (ConstraintViolation violation : constraintViolationException.getConstraintViolations()) { - logMsg.append(" Invalid value: <<<").append(violation.getInvalidValue()).append(">>> for ").append(violation.getPropertyPath()).append(" at ").append(violation.getLeafBean()).append(" - ").append(violation.getMessage()); - userMsg.append(" Invalid value: <<<").append(violation.getInvalidValue()).append(">>> for ").append(violation.getPropertyPath()).append(" - ").append(violation.getMessage()); - } - logger.warning(logMsg.toString()); - return errorResponse(Response.Status.BAD_REQUEST, "User id " + id + " could not be converted from Shibboleth to BuiltIn: " + userMsg.toString()); - } else { - return errorResponse(Response.Status.INTERNAL_SERVER_ERROR, "User id " + id + " cannot be converted due to unexpected exception: " + ex); - } - } - if (builtinUser == null) { - return errorResponse(Response.Status.BAD_REQUEST, "User id " + id + " could not be converted from Shibboleth to BuiltIn"); - } - try { - /** - * @todo Should this logic be moved to the - * authSvc.convertShibToBuiltIn() method? - */ - lookup.setAuthenticationProviderId(BuiltinAuthenticationProvider.PROVIDER_ID); - lookup.setPersistentUserId(userToConvert.getUserIdentifier()); - em.persist(lookup); - userToConvert.setEmail(newEmailAddress); - em.persist(userToConvert); - em.flush(); - } catch (Throwable ex) { - while (ex.getCause() != null) { - ex = ex.getCause(); - } - if (ex instanceof SQLException) { - String msg = "User id " + id + " only half converted from Shibboleth to BuiltIn and may not be able to log in. Manual changes may be necessary on 'authenticationproviderid' and 'authenticationproviderid' on 'authenticateduserlookup' table and 'email' on 'authenticateduser' table."; - logger.warning(msg); - return errorResponse(Response.Status.BAD_REQUEST, msg); - } else { - return errorResponse(Response.Status.INTERNAL_SERVER_ERROR, "User id " + id + " only half converted from Shibboleth to BuiltIn and may not be able to log in due to unexpected exception: " + ex.getClass().getName()); + BuiltinUser builtinUser = authSvc.convertShibToBuiltIn(id, newEmailAddress); + if (builtinUser == null) { + return errorResponse(Response.Status.BAD_REQUEST, "User id " + id + " could not be converted from Shibboleth to BuiltIn. An Exception was not thrown."); } + JsonObjectBuilder output = Json.createObjectBuilder(); + output.add("email", builtinUser.getEmail()); + output.add("username", builtinUser.getUserName()); + return okResponse(output); + } catch (Exception ex) { + String msg = "User id " + id + " could not be converted from Shibboleth to BuiltIn. Details from Exception: " + ex; + logger.info(msg); + return errorResponse(Response.Status.BAD_REQUEST, msg); } - JsonObjectBuilder output = Json.createObjectBuilder(); - output.add("email", builtinUser.getEmail()); - output.add("username", builtinUser.getUserName()); - return okResponse(output); } /** 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 8303bc32701..a9274c075ad 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationServiceBean.java @@ -18,6 +18,7 @@ import edu.harvard.iq.dataverse.authorization.providers.shib.ShibAuthenticationProvider; import edu.harvard.iq.dataverse.authorization.users.ApiToken; import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser; +import java.sql.SQLException; import java.sql.Timestamp; import java.util.Calendar; import java.util.Date; @@ -504,30 +505,70 @@ public AuthenticatedUser convertBuiltInToShib(AuthenticatedUser builtInUserToCon } /** - * @param authenticatedUser The AuthenticatedUser (Shibboleth user) to - * convert to a BuiltinUser. + * @param idOfAuthUserToConvert The id of the AuthenticatedUser (Shibboleth + * user) to convert to a BuiltinUser. * @param newEmailAddress The new email address that will be used instead of * the user's old email address from the institution that they have left. * @return BuiltinUser - * @throws java.lang.Exception You must catch a potential - * ConstraintViolationException due to Bean Validation (non-null, etc.) on - * the entities. Report these back to the superuser. + * @throws java.lang.Exception You must catch and report back to the user (a + * superuser) any Exceptions. */ - public BuiltinUser convertShibToBuiltIn(AuthenticatedUser authenticatedUser, String newEmailAddress) throws Exception { + public BuiltinUser convertShibToBuiltIn(Long idOfAuthUserToConvert, String newEmailAddress) throws Exception { + AuthenticatedUser authenticatedUser = findByID(idOfAuthUserToConvert); + if (authenticatedUser == null) { + throw new Exception("User id " + idOfAuthUserToConvert + " not found."); + } BuiltinUser builtinUser = new BuiltinUser(); builtinUser.setUserName(authenticatedUser.getUserIdentifier()); builtinUser.setFirstName(authenticatedUser.getFirstName()); builtinUser.setLastName(authenticatedUser.getLastName()); + // Bean Validation will check for null and invalid email addresses builtinUser.setEmail(newEmailAddress); - /** - * @todo If there are violations, don't even try to persist the user. - * Report the violations. Write tests around this. - */ ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); Validator validator = factory.getValidator(); Set> violations = validator.validate(builtinUser); - logger.fine("constraint violation count: " + violations.size()); - builtinUser = builtinUserServiceBean.save(builtinUser); + int numViolations = violations.size(); + if (numViolations > 0) { + StringBuilder logMsg = new StringBuilder(); + for (ConstraintViolation violation : violations) { + logMsg.append(" Invalid value: <<<").append(violation.getInvalidValue()).append(">>> for ").append(violation.getPropertyPath()).append(" at ").append(violation.getLeafBean()).append(" - ").append(violation.getMessage()); + } + throw new Exception("User id " + idOfAuthUserToConvert + " cannot be converted from Shibboleth to BuiltIn because of constraint violations on the BuiltIn user that would be created: " + numViolations + ". Details: " + logMsg); + } + try { + builtinUser = builtinUserServiceBean.save(builtinUser); + } catch (IllegalArgumentException ex) { + throw new Exception("User id " + idOfAuthUserToConvert + " cannot be converted from Shibboleth to BuiltIn because of an IllegalArgumentException creating the row in the builtinuser table: " + ex); + } + AuthenticatedUserLookup lookup = authenticatedUser.getAuthenticatedUserLookup(); + if (lookup == null) { + throw new Exception("User id " + idOfAuthUserToConvert + " does not have an 'authenticateduserlookup' row"); + } + String providerId = lookup.getAuthenticationProviderId(); + if (providerId == null) { + throw new Exception("User id " + idOfAuthUserToConvert + " provider id is null."); + } + String shibProviderId = ShibAuthenticationProvider.PROVIDER_ID; + if (!providerId.equals(shibProviderId)) { + throw new Exception("User id " + idOfAuthUserToConvert + " cannot be converted from Shibboleth to BuiltIn because current provider id is '" + providerId + "' rather than '" + shibProviderId + "'."); + } + try { + lookup.setAuthenticationProviderId(BuiltinAuthenticationProvider.PROVIDER_ID); + lookup.setPersistentUserId(authenticatedUser.getUserIdentifier()); + em.persist(lookup); + authenticatedUser.setEmail(newEmailAddress); + em.persist(authenticatedUser); + em.flush(); + } catch (Throwable ex) { + while (ex.getCause() != null) { + ex = ex.getCause(); + } + if (ex instanceof SQLException) { + throw new Exception("User id " + idOfAuthUserToConvert + " could not be converted from Shibboleth to BuiltIn due to SQLException. Duplicate email? Details of the SQLException: " + ex); + } else { + throw new Exception("User id " + idOfAuthUserToConvert + " could not be converted from Shibboleth to BuiltIn due to unexpected exception: " + ex); + } + } return builtinUser; } From cd2b9cca0ae555f6a55a8c1758b1b1af51ac776c Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Mon, 16 May 2016 09:48:07 -0400 Subject: [PATCH 2/2] Shib: add explicit check for duplicate email #2915 --- .../edu/harvard/iq/dataverse/api/Admin.java | 10 +++++-- .../AuthenticationServiceBean.java | 27 +++++++------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Admin.java b/src/main/java/edu/harvard/iq/dataverse/api/Admin.java index fd8618b23f8..0c131871282 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Admin.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Admin.java @@ -286,8 +286,14 @@ public Response convertShibUserToBuiltin(@PathParam("id") Long id, String newEma output.add("email", builtinUser.getEmail()); output.add("username", builtinUser.getUserName()); return okResponse(output); - } catch (Exception ex) { - String msg = "User id " + id + " could not be converted from Shibboleth to BuiltIn. Details from Exception: " + ex; + } catch (Throwable ex) { + StringBuilder sb = new StringBuilder(); + sb.append(ex + " "); + while (ex.getCause() != null) { + ex = ex.getCause(); + sb.append(ex + " "); + } + String msg = "User id " + id + " could not be converted from Shibboleth to BuiltIn. Details from Exception: " + sb; logger.info(msg); return errorResponse(Response.Status.BAD_REQUEST, msg); } 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 a9274c075ad..91544cb5d73 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationServiceBean.java @@ -518,6 +518,10 @@ public BuiltinUser convertShibToBuiltIn(Long idOfAuthUserToConvert, String newEm if (authenticatedUser == null) { throw new Exception("User id " + idOfAuthUserToConvert + " not found."); } + AuthenticatedUser existingUserWithSameEmail = getAuthenticatedUserByEmail(newEmailAddress); + if (existingUserWithSameEmail != null) { + throw new Exception("User id " + idOfAuthUserToConvert + " (" + authenticatedUser.getIdentifier() + ") cannot be converted from Shibboleth to BuiltIn because the email address " + newEmailAddress + " is already in use by user id " + existingUserWithSameEmail.getId() + " (" + existingUserWithSameEmail.getIdentifier() + ")."); + } BuiltinUser builtinUser = new BuiltinUser(); builtinUser.setUserName(authenticatedUser.getUserIdentifier()); builtinUser.setFirstName(authenticatedUser.getFirstName()); @@ -552,23 +556,12 @@ public BuiltinUser convertShibToBuiltIn(Long idOfAuthUserToConvert, String newEm if (!providerId.equals(shibProviderId)) { throw new Exception("User id " + idOfAuthUserToConvert + " cannot be converted from Shibboleth to BuiltIn because current provider id is '" + providerId + "' rather than '" + shibProviderId + "'."); } - try { - lookup.setAuthenticationProviderId(BuiltinAuthenticationProvider.PROVIDER_ID); - lookup.setPersistentUserId(authenticatedUser.getUserIdentifier()); - em.persist(lookup); - authenticatedUser.setEmail(newEmailAddress); - em.persist(authenticatedUser); - em.flush(); - } catch (Throwable ex) { - while (ex.getCause() != null) { - ex = ex.getCause(); - } - if (ex instanceof SQLException) { - throw new Exception("User id " + idOfAuthUserToConvert + " could not be converted from Shibboleth to BuiltIn due to SQLException. Duplicate email? Details of the SQLException: " + ex); - } else { - throw new Exception("User id " + idOfAuthUserToConvert + " could not be converted from Shibboleth to BuiltIn due to unexpected exception: " + ex); - } - } + lookup.setAuthenticationProviderId(BuiltinAuthenticationProvider.PROVIDER_ID); + lookup.setPersistentUserId(authenticatedUser.getUserIdentifier()); + em.persist(lookup); + authenticatedUser.setEmail(newEmailAddress); + em.persist(authenticatedUser); + em.flush(); return builtinUser; }