From fee5c2a0541e19052358939eb21ca12cd5066737 Mon Sep 17 00:00:00 2001 From: bsilverstein Date: Fri, 15 Jul 2016 13:52:13 -0400 Subject: [PATCH] Successful / simplified api test for email confirmation #2170 --- scripts/database/upgrades/confirmemail.sql | 1 + .../edu/harvard/iq/dataverse/api/Admin.java | 16 +++ .../users/AuthenticatedUser.java | 8 -- .../confirmemail/ConfirmEmailData.java | 2 - .../confirmemail/ConfirmEmailPage.java | 4 +- .../iq/dataverse/util/json/JsonPrinter.java | 4 +- .../util/json/NullSafeJsonBuilder.java | 8 +- .../iq/dataverse/api/ConfirmEmailIT.java | 120 ++++++++++-------- 8 files changed, 91 insertions(+), 72 deletions(-) create mode 100644 scripts/database/upgrades/confirmemail.sql diff --git a/scripts/database/upgrades/confirmemail.sql b/scripts/database/upgrades/confirmemail.sql new file mode 100644 index 00000000000..6296fca8a5f --- /dev/null +++ b/scripts/database/upgrades/confirmemail.sql @@ -0,0 +1 @@ +ALTER TABLE authenticateduser ADD COLUMN emailconfirmed timestamp without time zone; 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 53e1aba7537..dfa3f64ad6f 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Admin.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Admin.java @@ -18,6 +18,8 @@ import edu.harvard.iq.dataverse.authorization.providers.shib.ShibServiceBean; import edu.harvard.iq.dataverse.authorization.providers.shib.ShibUtil; import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser; +import edu.harvard.iq.dataverse.confirmemail.ConfirmEmailData; +import edu.harvard.iq.dataverse.confirmemail.ConfirmEmailExecResponse; import edu.harvard.iq.dataverse.engine.command.impl.PublishDataverseCommand; import edu.harvard.iq.dataverse.settings.Setting; import javax.json.Json; @@ -529,5 +531,19 @@ public Response getConfirmEmailToken(@PathParam("token") String token) { return null; } + @Path("confirmEmail/{token}") + @POST + public Response confirmTheEmail(@PathParam("token") String token) { + System.out.println("Calling processToken"); + ConfirmEmailExecResponse confirmEmailExecResponse = confirmEmailSvc.processToken(token); + System.out.println("Done calling processToken"); + ConfirmEmailData confirmEmailData = confirmEmailExecResponse.getConfirmEmailData(); + if (confirmEmailData == null) { + return errorResponse(Status.NOT_FOUND, "Invalid token: " + token); + } + AuthenticatedUser authenticatedUser = confirmEmailData.getAuthenticatedUser(); + return okResponse(jsonForAuthUser(authenticatedUser)); +// return okResponse("found user " +authenticatedUser.getId()); + } } diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/users/AuthenticatedUser.java b/src/main/java/edu/harvard/iq/dataverse/authorization/users/AuthenticatedUser.java index 2e0029e47f1..3daffd8dc22 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/users/AuthenticatedUser.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/users/AuthenticatedUser.java @@ -70,7 +70,6 @@ public class AuthenticatedUser implements User, Serializable { private String position; private String lastName; private String firstName; - private String confirmToken; @Column(nullable = true) private Timestamp emailConfirmed; @@ -83,13 +82,6 @@ public void setEmailConfirmed(Timestamp emailConfirmed) { } private boolean superuser; - public String getConfirmToken() { - return confirmToken; - } - - public void setConfirmToken(String confirmToken){ - this.confirmToken = confirmToken; - } /** * @todo Remove? Check for accuracy? For Solr JOINs we used to care about * the modification times of users but now we don't index users at all. diff --git a/src/main/java/edu/harvard/iq/dataverse/confirmemail/ConfirmEmailData.java b/src/main/java/edu/harvard/iq/dataverse/confirmemail/ConfirmEmailData.java index 8fdb52afc3a..6fa0eb78335 100644 --- a/src/main/java/edu/harvard/iq/dataverse/confirmemail/ConfirmEmailData.java +++ b/src/main/java/edu/harvard/iq/dataverse/confirmemail/ConfirmEmailData.java @@ -64,8 +64,6 @@ public ConfirmEmailData(AuthenticatedUser anAuthenticatedUser) { created = new Timestamp(nowInMilliseconds); long ONE_MINUTE_IN_MILLISECONDS = 60000; /** - * @todo: make the token's time before expiration way longer - * * @todo: use database setting instead of jvm option for line 75 configurable expiration value */ diff --git a/src/main/java/edu/harvard/iq/dataverse/confirmemail/ConfirmEmailPage.java b/src/main/java/edu/harvard/iq/dataverse/confirmemail/ConfirmEmailPage.java index c90d8ed0703..804cb1c06a7 100644 --- a/src/main/java/edu/harvard/iq/dataverse/confirmemail/ConfirmEmailPage.java +++ b/src/main/java/edu/harvard/iq/dataverse/confirmemail/ConfirmEmailPage.java @@ -20,8 +20,6 @@ import org.hibernate.validator.constraints.NotBlank; /** - * @todo: Figure out what's going on with confirmEmail method - * Determine whether actionlog recording will stay or not * * @author bsilverstein */ @@ -34,7 +32,7 @@ public class ConfirmEmailPage implements java.io.Serializable { @EJB ConfirmEmailServiceBean confirmEmailService; - @EJB //maybe make a shib and builtin user service then divide later? + @EJB AuthenticationServiceBean dataverseUserService; @EJB DataverseServiceBean dataverseService; diff --git a/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java b/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java index 1ae642cd7fc..7cb28db8250 100644 --- a/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java +++ b/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java @@ -87,8 +87,8 @@ public static JsonObjectBuilder jsonForAuthUser(AuthenticatedUser authenticatedU .add("superuser", authenticatedUser.isSuperuser()) .add("affiliation", authenticatedUser.getAffiliation()) .add("position", authenticatedUser.getPosition()) - .add("persistentUserId", authenticatedUser.getAuthenticatedUserLookup().getPersistentUserId()) - .add("confirmToken", authenticatedUser.getConfirmToken()) + .add("persistentUserId", authenticatedUser.getAuthenticatedUserLookup().getPersistentUserId()) + .add("emailLastConfirmed", authenticatedUser.getEmailConfirmed()) .add("authenticationProviderId", authenticatedUser.getAuthenticatedUserLookup().getAuthenticationProviderId()); } diff --git a/src/main/java/edu/harvard/iq/dataverse/util/json/NullSafeJsonBuilder.java b/src/main/java/edu/harvard/iq/dataverse/util/json/NullSafeJsonBuilder.java index 452a5366576..25c57b5cf7d 100644 --- a/src/main/java/edu/harvard/iq/dataverse/util/json/NullSafeJsonBuilder.java +++ b/src/main/java/edu/harvard/iq/dataverse/util/json/NullSafeJsonBuilder.java @@ -1,8 +1,10 @@ package edu.harvard.iq.dataverse.util.json; import edu.harvard.iq.dataverse.DatasetField; +import edu.harvard.iq.dataverse.api.Util; import java.math.BigDecimal; import java.math.BigInteger; +import java.sql.Timestamp; import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObject; @@ -115,6 +117,8 @@ public NullSafeJsonBuilder addStrValue( String name, DatasetField field ) { public JsonObject build() { return delegate.build(); } - - + + public NullSafeJsonBuilder add(String name, Timestamp timestamp) { + return (timestamp != null) ? add(name, Util.getDateTimeFormat().format(timestamp)) : this; + } } diff --git a/src/test/java/edu/harvard/iq/dataverse/api/ConfirmEmailIT.java b/src/test/java/edu/harvard/iq/dataverse/api/ConfirmEmailIT.java index 1e691bbe189..d4d1e9fe5ae 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/ConfirmEmailIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/ConfirmEmailIT.java @@ -23,9 +23,9 @@ * @todo: Develop test to reflect access restrictions without confirmation */ public class ConfirmEmailIT { - + private static final Logger logger = Logger.getLogger(ConfirmEmailIT.class.getCanonicalName()); - + private static final String builtinUserKey = "burrito"; private static final String idKey = "id"; private static final String usernameKey = "userName"; @@ -33,47 +33,78 @@ public class ConfirmEmailIT { private static final AuthenticatedUser authenticatedUser = new AuthenticatedUser(); private static final ConfirmEmailData emailData = new ConfirmEmailData(authenticatedUser); private static final String confirmToken = getConfirmEmailToken(emailData); - + @BeforeClass public static void setUp() { - RestAssured.baseURI = UtilIT.getRestAssuredBaseUri(); + RestAssured.baseURI = UtilIT.getRestAssuredBaseUri(); } - - - - @Test - public void testConfirm() { - // Can't seem to get timestamp to appear in authenticated user Json output - String email = null; - Response createUserToConfirm = createUser(getRandomUsername(), "firstName", "lastName", email); - createUserToConfirm.prettyPrint(); - createUserToConfirm.then().assertThat() - .statusCode(200); - - long userIdToConfirm = JsonPath.from(createUserToConfirm.body().asString()).getLong("data.authenticatedUser.id"); - String userToConfirmApiToken = JsonPath.from(createUserToConfirm.body().asString()).getString("data.apiToken"); - String usernameToConfirm = JsonPath.from(createUserToConfirm.body().asString()).getString("data.user.userName"); - Response getApiToken = getApiTokenUsingUsername(usernameToConfirm, usernameToConfirm); - getApiToken.then().assertThat() - .statusCode(200); - } - + @Test + public void testConfirm() { + // Can't seem to get timestamp to appear in authenticated user Json output + /** + * + */ + String email = null; + + Response createUserToConfirm = createUser(getRandomUsername(), "firstName", "lastName", email); + createUserToConfirm.prettyPrint(); + // do not expose confirm email token to user, just in email URL + String confirmEmailToken = JsonPath.from(createUserToConfirm.body().asString()).getString("data.confirmEmailToken"); + createUserToConfirm.then().assertThat() + .statusCode(200); + + //redundant? + long userIdToConfirm = JsonPath.from(createUserToConfirm.body().asString()).getLong("data.authenticatedUser.id"); + String userToConfirmApiToken = JsonPath.from(createUserToConfirm.body().asString()).getString("data.apiToken"); + String usernameToConfirm = JsonPath.from(createUserToConfirm.body().asString()).getString("data.user.userName"); + Response getApiToken = getApiTokenUsingUsername(usernameToConfirm, usernameToConfirm); + getApiToken.then().assertThat() + .statusCode(200); + String token = "noSuchToken"; + + /** + * @todo: Superuser GET confirm email token based on user's database ID + * (primary key). This can answer questions the superuser may have, such + * as, "Did the user's token expire?" + */ +// Response getConfirmEmailData = given() +// .get("/api/admin/confirmEmail/" + 42); + Response noSuchToken = given() + .post("/api/admin/confirmEmail/" + token); + noSuchToken.prettyPrint(); + // todo assert "Invalid token: noSuchToken" and 404 + /** + * + * User will call a second method within admin API to POST token to new + * endpoint /api/admin/confirmEmail/{token} + * + */ + + System.out.println("real token: " + confirmEmailToken); + // This is simulating the user clicking the URL from their email client. + Response confirmEmail = given() + .post("/api/admin/confirmEmail/" + confirmEmailToken); + confirmEmail.prettyPrint(); + + //todo: superuser checks that email has been confirmed based on user id + } + private Response createUser(String username, String firstName, String lastName, String email) { String userAsJson = getUserAsJsonString(username, firstName, lastName, email); String password = getPassword(userAsJson); Response response = given() - .body(userAsJson) + .body(userAsJson) .contentType(ContentType.JSON) .post("/api/builtin-users?key=" + builtinUserKey + "&password=" + password); return response; } - + private static String getRandomUsername() { return UUID.randomUUID().toString().substring(0, 8); } - + private static String getUserAsJsonString(String username, String firstName, String lastName, String email) { JsonObjectBuilder builder = Json.createObjectBuilder(); builder.add(usernameKey, username); @@ -84,52 +115,31 @@ private static String getUserAsJsonString(String username, String firstName, Str } else { builder.add(emailKey, email); } - + String userAsJson = builder.build().toString(); logger.fine("User to create: " + userAsJson); return userAsJson; } - //May be redundant / unusable? - private static String getAuthUserAsJsonString(AuthenticatedUser authenticatedUser){ - JsonObjectBuilder authenticatedUserBuilder = JsonPrinter.jsonForAuthUser(authenticatedUser); - authenticatedUserBuilder.add("id", authenticatedUser.getId()); - authenticatedUserBuilder.add("identifier", authenticatedUser.getIdentifier()); - authenticatedUserBuilder.add("displayName", authenticatedUser.getDisplayInfo().getTitle()); - authenticatedUserBuilder.add("firstName", authenticatedUser.getFirstName()); - authenticatedUserBuilder.add("lastName", authenticatedUser.getLastName()); - authenticatedUserBuilder.add("email", authenticatedUser.getEmail()); - authenticatedUserBuilder.add("superuser", authenticatedUser.isSuperuser()); - authenticatedUserBuilder.add("affiliation", authenticatedUser.getAffiliation()); - authenticatedUserBuilder.add("position", authenticatedUser.getPosition()); - authenticatedUserBuilder.add("persistentUserId", authenticatedUser.getAuthenticatedUserLookup().getPersistentUserId()); - authenticatedUserBuilder.add("confirmToken", authenticatedUser.getConfirmToken()); - authenticatedUserBuilder.add("authenticationProviderId", authenticatedUser.getAuthenticatedUserLookup().getAuthenticationProviderId()); - - String authenticatedUserAsJson = authenticatedUserBuilder.build().toString(); - logger.fine("Authenticated User to create: " + authenticatedUserAsJson); - return authenticatedUserAsJson; - - } - + private static String getPassword(String jsonStr) { String password = JsonPath.from(jsonStr).get(usernameKey); return password; } - + private static String getEmailFromUserName(String username) { return username + "@mailinator.com"; } - - private static String getConfirmEmailToken(ConfirmEmailData emailData){ + + private static String getConfirmEmailToken(ConfirmEmailData emailData) { String confirmToken = emailData.getToken(); return confirmToken; } - + private Response getApiTokenUsingUsername(String username, String password) { Response response = given() .contentType(ContentType.JSON) .get("/api/builtin-users/" + username + "/api-token?username=" + username + "&password=" + password); return response; } - + }