@Test void testCreateVerificationEmailSuccess() { when(usersDao.findByEmail(anyString())).thenReturn(unverifiedMockUser); when(usersDao.update(anyString(), any(User.class))).thenReturn(unverifiedMockUser); when(emailService.sendVerificationEmail(any(Email.class), anyString())).thenReturn(true); Response response = resource.createVerificationEmail(uriInfo, key, "test@test.com", "password"); User result = (User) response.getEntity(); assertAll("Assert successful send email", () -> assertEquals(response.getStatusInfo(), Response.Status.OK), () -> assertEquals(unverifiedMockUser, result)); }
@POST @Operation( summary = "Send a verification email to the specified email address", description = "Initiates the user verification process by sending a verification email " + "to the email address provided as a query parameter. The user in the database will be " + "updated to include a unique verification token that is sent along with the email.", tags = { "verify" }, responses = { @ApiResponse(responseCode = "200", description = "The operation was successfully completed", content = @Content( mediaType = "application/json", schema = @Schema(implementation = User.class))), @ApiResponse(responseCode = "400", description = "The send email request was malformed"), @ApiResponse(responseCode = "401", description = "The request was unauthorized"), @ApiResponse(responseCode = "404", description = "The user with the given email address was not found in the database"), @ApiResponse(responseCode = "500", description = "The database rejected the request for an unknown reason"), @ApiResponse(responseCode = "503", description = "The database is currently unavailable") }) @Metered(name = "send-email-requests") public Response createVerificationEmail( @Context UriInfo uriInfo, @Parameter(hidden = true) @Auth Principal auth, @Parameter(description = "The email address of the user to send the email.", required = true) @QueryParam("email") String email, @Parameter(description = "The password of the user, required if " + "headerPasswordCheck is enabled.") @HeaderParam("password") String password) { try { requestValidator.validate(password, email, false); } catch (ValidationException e) { return Response.status(Response.Status.BAD_REQUEST) .entity(e.getMessage()).build(); } LOG.info("Attempting to send verification email to user {}", email); User user; try { user = usersDao.findByEmail(email); } catch (DatabaseException e) { LOG.error("Error retrieving user {} in database. Caused by: {}", email, e.getErrorKind()); return e.getErrorKind().buildResponse(email); } if (requestValidator.isPasswordHeaderCheckEnabled() && !hashService.isMatch(password, user.getPassword())) { LOG.warn("Incorrect password parameter for user {} in database.", user.getEmail()); return Response.status(Response.Status.UNAUTHORIZED) .entity("Unable to validate user with provided credentials.").build(); } String token = generateVerificationToken(); User updatedUser = new User( new Email(user.getEmail().getAddress(), false, token), user.getPassword(), user.getProperties()); User result; try { result = usersDao.update(user.getEmail().getAddress(), updatedUser); } catch (DatabaseException e) { LOG.error("Error posting user {} to the database. Caused by {}", user.getEmail(), e.getErrorKind()); return e.getErrorKind().buildResponse(user.getEmail().getAddress()); } String verificationUrl = uriInfo.getBaseUriBuilder().path("/verify") .queryParam("email", result.getEmail().getAddress()) .queryParam("token", token) .queryParam("response_type", "html") .build().toString(); boolean emailResult = emailService.sendVerificationEmail(result.getEmail(), verificationUrl); if (!emailResult) { LOG.error("Error sending email to address {}", result.getEmail().getAddress()); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("An error occurred while attempting to send an email.").build(); } LOG.info("Successfully sent verification email to user {}.", email); return Response.ok(result).build(); }
VerificationResource { @POST @Operation( summary = "Send a verification email to the specified email address", description = "Initiates the user verification process by sending a verification email " + "to the email address provided as a query parameter. The user in the database will be " + "updated to include a unique verification token that is sent along with the email.", tags = { "verify" }, responses = { @ApiResponse(responseCode = "200", description = "The operation was successfully completed", content = @Content( mediaType = "application/json", schema = @Schema(implementation = User.class))), @ApiResponse(responseCode = "400", description = "The send email request was malformed"), @ApiResponse(responseCode = "401", description = "The request was unauthorized"), @ApiResponse(responseCode = "404", description = "The user with the given email address was not found in the database"), @ApiResponse(responseCode = "500", description = "The database rejected the request for an unknown reason"), @ApiResponse(responseCode = "503", description = "The database is currently unavailable") }) @Metered(name = "send-email-requests") public Response createVerificationEmail( @Context UriInfo uriInfo, @Parameter(hidden = true) @Auth Principal auth, @Parameter(description = "The email address of the user to send the email.", required = true) @QueryParam("email") String email, @Parameter(description = "The password of the user, required if " + "headerPasswordCheck is enabled.") @HeaderParam("password") String password) { try { requestValidator.validate(password, email, false); } catch (ValidationException e) { return Response.status(Response.Status.BAD_REQUEST) .entity(e.getMessage()).build(); } LOG.info("Attempting to send verification email to user {}", email); User user; try { user = usersDao.findByEmail(email); } catch (DatabaseException e) { LOG.error("Error retrieving user {} in database. Caused by: {}", email, e.getErrorKind()); return e.getErrorKind().buildResponse(email); } if (requestValidator.isPasswordHeaderCheckEnabled() && !hashService.isMatch(password, user.getPassword())) { LOG.warn("Incorrect password parameter for user {} in database.", user.getEmail()); return Response.status(Response.Status.UNAUTHORIZED) .entity("Unable to validate user with provided credentials.").build(); } String token = generateVerificationToken(); User updatedUser = new User( new Email(user.getEmail().getAddress(), false, token), user.getPassword(), user.getProperties()); User result; try { result = usersDao.update(user.getEmail().getAddress(), updatedUser); } catch (DatabaseException e) { LOG.error("Error posting user {} to the database. Caused by {}", user.getEmail(), e.getErrorKind()); return e.getErrorKind().buildResponse(user.getEmail().getAddress()); } String verificationUrl = uriInfo.getBaseUriBuilder().path("/verify") .queryParam("email", result.getEmail().getAddress()) .queryParam("token", token) .queryParam("response_type", "html") .build().toString(); boolean emailResult = emailService.sendVerificationEmail(result.getEmail(), verificationUrl); if (!emailResult) { LOG.error("Error sending email to address {}", result.getEmail().getAddress()); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("An error occurred while attempting to send an email.").build(); } LOG.info("Successfully sent verification email to user {}.", email); return Response.ok(result).build(); } }
VerificationResource { @POST @Operation( summary = "Send a verification email to the specified email address", description = "Initiates the user verification process by sending a verification email " + "to the email address provided as a query parameter. The user in the database will be " + "updated to include a unique verification token that is sent along with the email.", tags = { "verify" }, responses = { @ApiResponse(responseCode = "200", description = "The operation was successfully completed", content = @Content( mediaType = "application/json", schema = @Schema(implementation = User.class))), @ApiResponse(responseCode = "400", description = "The send email request was malformed"), @ApiResponse(responseCode = "401", description = "The request was unauthorized"), @ApiResponse(responseCode = "404", description = "The user with the given email address was not found in the database"), @ApiResponse(responseCode = "500", description = "The database rejected the request for an unknown reason"), @ApiResponse(responseCode = "503", description = "The database is currently unavailable") }) @Metered(name = "send-email-requests") public Response createVerificationEmail( @Context UriInfo uriInfo, @Parameter(hidden = true) @Auth Principal auth, @Parameter(description = "The email address of the user to send the email.", required = true) @QueryParam("email") String email, @Parameter(description = "The password of the user, required if " + "headerPasswordCheck is enabled.") @HeaderParam("password") String password) { try { requestValidator.validate(password, email, false); } catch (ValidationException e) { return Response.status(Response.Status.BAD_REQUEST) .entity(e.getMessage()).build(); } LOG.info("Attempting to send verification email to user {}", email); User user; try { user = usersDao.findByEmail(email); } catch (DatabaseException e) { LOG.error("Error retrieving user {} in database. Caused by: {}", email, e.getErrorKind()); return e.getErrorKind().buildResponse(email); } if (requestValidator.isPasswordHeaderCheckEnabled() && !hashService.isMatch(password, user.getPassword())) { LOG.warn("Incorrect password parameter for user {} in database.", user.getEmail()); return Response.status(Response.Status.UNAUTHORIZED) .entity("Unable to validate user with provided credentials.").build(); } String token = generateVerificationToken(); User updatedUser = new User( new Email(user.getEmail().getAddress(), false, token), user.getPassword(), user.getProperties()); User result; try { result = usersDao.update(user.getEmail().getAddress(), updatedUser); } catch (DatabaseException e) { LOG.error("Error posting user {} to the database. Caused by {}", user.getEmail(), e.getErrorKind()); return e.getErrorKind().buildResponse(user.getEmail().getAddress()); } String verificationUrl = uriInfo.getBaseUriBuilder().path("/verify") .queryParam("email", result.getEmail().getAddress()) .queryParam("token", token) .queryParam("response_type", "html") .build().toString(); boolean emailResult = emailService.sendVerificationEmail(result.getEmail(), verificationUrl); if (!emailResult) { LOG.error("Error sending email to address {}", result.getEmail().getAddress()); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("An error occurred while attempting to send an email.").build(); } LOG.info("Successfully sent verification email to user {}.", email); return Response.ok(result).build(); } @Inject  VerificationResource(UsersDao usersDao,
                              RequestValidator requestValidator,
                              EmailService emailService,
                              HashService hashService); }
VerificationResource { @POST @Operation( summary = "Send a verification email to the specified email address", description = "Initiates the user verification process by sending a verification email " + "to the email address provided as a query parameter. The user in the database will be " + "updated to include a unique verification token that is sent along with the email.", tags = { "verify" }, responses = { @ApiResponse(responseCode = "200", description = "The operation was successfully completed", content = @Content( mediaType = "application/json", schema = @Schema(implementation = User.class))), @ApiResponse(responseCode = "400", description = "The send email request was malformed"), @ApiResponse(responseCode = "401", description = "The request was unauthorized"), @ApiResponse(responseCode = "404", description = "The user with the given email address was not found in the database"), @ApiResponse(responseCode = "500", description = "The database rejected the request for an unknown reason"), @ApiResponse(responseCode = "503", description = "The database is currently unavailable") }) @Metered(name = "send-email-requests") public Response createVerificationEmail( @Context UriInfo uriInfo, @Parameter(hidden = true) @Auth Principal auth, @Parameter(description = "The email address of the user to send the email.", required = true) @QueryParam("email") String email, @Parameter(description = "The password of the user, required if " + "headerPasswordCheck is enabled.") @HeaderParam("password") String password) { try { requestValidator.validate(password, email, false); } catch (ValidationException e) { return Response.status(Response.Status.BAD_REQUEST) .entity(e.getMessage()).build(); } LOG.info("Attempting to send verification email to user {}", email); User user; try { user = usersDao.findByEmail(email); } catch (DatabaseException e) { LOG.error("Error retrieving user {} in database. Caused by: {}", email, e.getErrorKind()); return e.getErrorKind().buildResponse(email); } if (requestValidator.isPasswordHeaderCheckEnabled() && !hashService.isMatch(password, user.getPassword())) { LOG.warn("Incorrect password parameter for user {} in database.", user.getEmail()); return Response.status(Response.Status.UNAUTHORIZED) .entity("Unable to validate user with provided credentials.").build(); } String token = generateVerificationToken(); User updatedUser = new User( new Email(user.getEmail().getAddress(), false, token), user.getPassword(), user.getProperties()); User result; try { result = usersDao.update(user.getEmail().getAddress(), updatedUser); } catch (DatabaseException e) { LOG.error("Error posting user {} to the database. Caused by {}", user.getEmail(), e.getErrorKind()); return e.getErrorKind().buildResponse(user.getEmail().getAddress()); } String verificationUrl = uriInfo.getBaseUriBuilder().path("/verify") .queryParam("email", result.getEmail().getAddress()) .queryParam("token", token) .queryParam("response_type", "html") .build().toString(); boolean emailResult = emailService.sendVerificationEmail(result.getEmail(), verificationUrl); if (!emailResult) { LOG.error("Error sending email to address {}", result.getEmail().getAddress()); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("An error occurred while attempting to send an email.").build(); } LOG.info("Successfully sent verification email to user {}.", email); return Response.ok(result).build(); } @Inject  VerificationResource(UsersDao usersDao,
                              RequestValidator requestValidator,
                              EmailService emailService,
                              HashService hashService); @POST @Operation( summary = "Send a verification email to the specified email address", description = "Initiates the user verification process by sending a verification email " + "to the email address provided as a query parameter. The user in the database will be " + "updated to include a unique verification token that is sent along with the email.", tags = { "verify" }, responses = { @ApiResponse(responseCode = "200", description = "The operation was successfully completed", content = @Content( mediaType = "application/json", schema = @Schema(implementation = User.class))), @ApiResponse(responseCode = "400", description = "The send email request was malformed"), @ApiResponse(responseCode = "401", description = "The request was unauthorized"), @ApiResponse(responseCode = "404", description = "The user with the given email address was not found in the database"), @ApiResponse(responseCode = "500", description = "The database rejected the request for an unknown reason"), @ApiResponse(responseCode = "503", description = "The database is currently unavailable") }) @Metered(name = "send-email-requests") Response createVerificationEmail(
      @Context UriInfo uriInfo,
      @Parameter(hidden = true) @Auth Principal auth,
      @Parameter(description = "The email address of the user to send the email.", required = true)
          @QueryParam("email") String email,
      @Parameter(description = "The password of the user, required if "
          + "headerPasswordCheck is enabled.") @HeaderParam("password") String password); @GET @Operation( summary = "Verify a user email address", description = "Used to verify a user email. Typically, the user will click on this link in " + "their email to verify their account. Upon verification, the user object in the " + "database will be updated to indicate that the email address is verified.", tags = { "verify" }, responses = { @ApiResponse(responseCode = "200", description = "The operation was successfully completed", content = @Content( mediaType = "application/json", schema = @Schema(implementation = User.class))), @ApiResponse(responseCode = "303", description = "The request should be redirected to /verify/success"), @ApiResponse(responseCode = "400", description = "The verify request was malformed"), @ApiResponse(responseCode = "404", description = "The user was not found in the database"), @ApiResponse(responseCode = "500", description = "The database rejected the request for an unknown reason"), @ApiResponse(responseCode = "503", description = "The database is currently unavailable") }) @Metered(name = "verify-email-requests") Response verifyEmail(
      @Parameter(description = "The email address of the user to verify.", required = true)
          @QueryParam("email") String email,
      @Parameter(description = "The verification token that matches the one sent via email.",
          required = true) @QueryParam("token") String token,
      @Parameter(description = "The optional response type, either HTML or JSON. If HTML is "
          + "specified, the URL will redirect to /verify/success.") @QueryParam("response_type")
          @DefaultValue("json") ResponseType responseType); @POST @Path("/reset") @Operation( summary = "Reset a user's email verification status", description = "Resets the verification status of a user's email address to unverified.", tags = { "verify" }, responses = { @ApiResponse(responseCode = "200", description = "The user's verification status was successfully reset", content = @Content( mediaType = "application/json", schema = @Schema(implementation = User.class))), @ApiResponse(responseCode = "400", description = "The verify request was malformed"), @ApiResponse(responseCode = "401", description = "The request was unauthorized"), @ApiResponse(responseCode = "404", description = "The user was not found in the database"), @ApiResponse(responseCode = "500", description = "The database rejected the request for an unknown reason"), @ApiResponse(responseCode = "503", description = "The database is currently unavailable") }) @Metered(name = "reset-verification-requests") Response resetVerificationStatus(
      @Parameter(hidden = true) @Auth Principal auth,
      @Parameter(description = "The email address of the user to reset.", required = true)
          @QueryParam("email") String email,
      @Parameter(description = "The password of the user, required if "
          + "headerPasswordCheck is enabled.") @HeaderParam("password") String password); @GET @Path("/success") @Produces(MediaType.TEXT_HTML) @Operation( summary = "Get success HTML", description = "Returns an HTML success page that is shown after a user successfully " + "verifies their account. GET /verify will redirect to this URL if the response_type " + "query parameter is set to html.", tags = { "verify" }, responses = { @ApiResponse(responseCode = "200", description = "The operation was successful", content = @Content( mediaType = "text/html", schema = @Schema(example = "<!DOCTYPE html>\n" + "<html>\n" + " <div class=\"alert alert-success\">\n" + " <div align=\"center\"><strong>Success!</strong><br>Your account has been " + "verified.</div>\n" + " </div>\n" + " <link rel=\"stylesheet\" " + "href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css\"" + " />\n</html>"))) }) Response getSuccessHtml(); }
VerificationResource { @POST @Operation( summary = "Send a verification email to the specified email address", description = "Initiates the user verification process by sending a verification email " + "to the email address provided as a query parameter. The user in the database will be " + "updated to include a unique verification token that is sent along with the email.", tags = { "verify" }, responses = { @ApiResponse(responseCode = "200", description = "The operation was successfully completed", content = @Content( mediaType = "application/json", schema = @Schema(implementation = User.class))), @ApiResponse(responseCode = "400", description = "The send email request was malformed"), @ApiResponse(responseCode = "401", description = "The request was unauthorized"), @ApiResponse(responseCode = "404", description = "The user with the given email address was not found in the database"), @ApiResponse(responseCode = "500", description = "The database rejected the request for an unknown reason"), @ApiResponse(responseCode = "503", description = "The database is currently unavailable") }) @Metered(name = "send-email-requests") public Response createVerificationEmail( @Context UriInfo uriInfo, @Parameter(hidden = true) @Auth Principal auth, @Parameter(description = "The email address of the user to send the email.", required = true) @QueryParam("email") String email, @Parameter(description = "The password of the user, required if " + "headerPasswordCheck is enabled.") @HeaderParam("password") String password) { try { requestValidator.validate(password, email, false); } catch (ValidationException e) { return Response.status(Response.Status.BAD_REQUEST) .entity(e.getMessage()).build(); } LOG.info("Attempting to send verification email to user {}", email); User user; try { user = usersDao.findByEmail(email); } catch (DatabaseException e) { LOG.error("Error retrieving user {} in database. Caused by: {}", email, e.getErrorKind()); return e.getErrorKind().buildResponse(email); } if (requestValidator.isPasswordHeaderCheckEnabled() && !hashService.isMatch(password, user.getPassword())) { LOG.warn("Incorrect password parameter for user {} in database.", user.getEmail()); return Response.status(Response.Status.UNAUTHORIZED) .entity("Unable to validate user with provided credentials.").build(); } String token = generateVerificationToken(); User updatedUser = new User( new Email(user.getEmail().getAddress(), false, token), user.getPassword(), user.getProperties()); User result; try { result = usersDao.update(user.getEmail().getAddress(), updatedUser); } catch (DatabaseException e) { LOG.error("Error posting user {} to the database. Caused by {}", user.getEmail(), e.getErrorKind()); return e.getErrorKind().buildResponse(user.getEmail().getAddress()); } String verificationUrl = uriInfo.getBaseUriBuilder().path("/verify") .queryParam("email", result.getEmail().getAddress()) .queryParam("token", token) .queryParam("response_type", "html") .build().toString(); boolean emailResult = emailService.sendVerificationEmail(result.getEmail(), verificationUrl); if (!emailResult) { LOG.error("Error sending email to address {}", result.getEmail().getAddress()); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("An error occurred while attempting to send an email.").build(); } LOG.info("Successfully sent verification email to user {}.", email); return Response.ok(result).build(); } @Inject  VerificationResource(UsersDao usersDao,
                              RequestValidator requestValidator,
                              EmailService emailService,
                              HashService hashService); @POST @Operation( summary = "Send a verification email to the specified email address", description = "Initiates the user verification process by sending a verification email " + "to the email address provided as a query parameter. The user in the database will be " + "updated to include a unique verification token that is sent along with the email.", tags = { "verify" }, responses = { @ApiResponse(responseCode = "200", description = "The operation was successfully completed", content = @Content( mediaType = "application/json", schema = @Schema(implementation = User.class))), @ApiResponse(responseCode = "400", description = "The send email request was malformed"), @ApiResponse(responseCode = "401", description = "The request was unauthorized"), @ApiResponse(responseCode = "404", description = "The user with the given email address was not found in the database"), @ApiResponse(responseCode = "500", description = "The database rejected the request for an unknown reason"), @ApiResponse(responseCode = "503", description = "The database is currently unavailable") }) @Metered(name = "send-email-requests") Response createVerificationEmail(
      @Context UriInfo uriInfo,
      @Parameter(hidden = true) @Auth Principal auth,
      @Parameter(description = "The email address of the user to send the email.", required = true)
          @QueryParam("email") String email,
      @Parameter(description = "The password of the user, required if "
          + "headerPasswordCheck is enabled.") @HeaderParam("password") String password); @GET @Operation( summary = "Verify a user email address", description = "Used to verify a user email. Typically, the user will click on this link in " + "their email to verify their account. Upon verification, the user object in the " + "database will be updated to indicate that the email address is verified.", tags = { "verify" }, responses = { @ApiResponse(responseCode = "200", description = "The operation was successfully completed", content = @Content( mediaType = "application/json", schema = @Schema(implementation = User.class))), @ApiResponse(responseCode = "303", description = "The request should be redirected to /verify/success"), @ApiResponse(responseCode = "400", description = "The verify request was malformed"), @ApiResponse(responseCode = "404", description = "The user was not found in the database"), @ApiResponse(responseCode = "500", description = "The database rejected the request for an unknown reason"), @ApiResponse(responseCode = "503", description = "The database is currently unavailable") }) @Metered(name = "verify-email-requests") Response verifyEmail(
      @Parameter(description = "The email address of the user to verify.", required = true)
          @QueryParam("email") String email,
      @Parameter(description = "The verification token that matches the one sent via email.",
          required = true) @QueryParam("token") String token,
      @Parameter(description = "The optional response type, either HTML or JSON. If HTML is "
          + "specified, the URL will redirect to /verify/success.") @QueryParam("response_type")
          @DefaultValue("json") ResponseType responseType); @POST @Path("/reset") @Operation( summary = "Reset a user's email verification status", description = "Resets the verification status of a user's email address to unverified.", tags = { "verify" }, responses = { @ApiResponse(responseCode = "200", description = "The user's verification status was successfully reset", content = @Content( mediaType = "application/json", schema = @Schema(implementation = User.class))), @ApiResponse(responseCode = "400", description = "The verify request was malformed"), @ApiResponse(responseCode = "401", description = "The request was unauthorized"), @ApiResponse(responseCode = "404", description = "The user was not found in the database"), @ApiResponse(responseCode = "500", description = "The database rejected the request for an unknown reason"), @ApiResponse(responseCode = "503", description = "The database is currently unavailable") }) @Metered(name = "reset-verification-requests") Response resetVerificationStatus(
      @Parameter(hidden = true) @Auth Principal auth,
      @Parameter(description = "The email address of the user to reset.", required = true)
          @QueryParam("email") String email,
      @Parameter(description = "The password of the user, required if "
          + "headerPasswordCheck is enabled.") @HeaderParam("password") String password); @GET @Path("/success") @Produces(MediaType.TEXT_HTML) @Operation( summary = "Get success HTML", description = "Returns an HTML success page that is shown after a user successfully " + "verifies their account. GET /verify will redirect to this URL if the response_type " + "query parameter is set to html.", tags = { "verify" }, responses = { @ApiResponse(responseCode = "200", description = "The operation was successful", content = @Content( mediaType = "text/html", schema = @Schema(example = "<!DOCTYPE html>\n" + "<html>\n" + " <div class=\"alert alert-success\">\n" + " <div align=\"center\"><strong>Success!</strong><br>Your account has been " + "verified.</div>\n" + " </div>\n" + " <link rel=\"stylesheet\" " + "href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css\"" + " />\n</html>"))) }) Response getSuccessHtml();  }