diff --git a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/domain/discord/DiscordStartVerificationResponse.java b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/domain/discord/DiscordStartVerificationResponse.java index c37f405f3..aca7419d1 100644 --- a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/domain/discord/DiscordStartVerificationResponse.java +++ b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/domain/discord/DiscordStartVerificationResponse.java @@ -1,15 +1,19 @@ package org.cardano.foundation.voting.domain.discord; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import org.cardano.foundation.voting.domain.VerificationStatus; @Builder @Getter +@AllArgsConstructor public class DiscordStartVerificationResponse { - String eventId; - String discordIdHash; - VerificationStatus status; + private String eventId; + + private String discordIdHash; + + private VerificationStatus status; } diff --git a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/domain/entity/DiscordUserVerification.java b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/domain/entity/DiscordUserVerification.java index b0128d3b8..9c75a4779 100644 --- a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/domain/entity/DiscordUserVerification.java +++ b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/domain/entity/DiscordUserVerification.java @@ -9,29 +9,36 @@ import lombok.extern.slf4j.Slf4j; import org.cardano.foundation.voting.domain.VerificationStatus; +import javax.annotation.Nullable; import java.time.LocalDateTime; +import java.util.Optional; @Entity @Table(name = "discord_user_verification") @Slf4j @NoArgsConstructor @SuperBuilder -@Getter -@Setter @AllArgsConstructor public class DiscordUserVerification extends AbstractTimestampEntity { @Id @Column(name = "discord_id_hash", nullable = false) + @Getter + @Setter private String discordIdHash; @Column(name = "event_id", nullable = false) + @Getter + @Setter private String eventId; - @Column(name = "stake_address", nullable = false) + @Column(name = "stake_address") + @Nullable private String stakeAddress; @Column(name = "secret_code", nullable = false) + @Getter + @Setter private String secretCode; @Column(name = "status", nullable = false) @@ -46,6 +53,14 @@ public class DiscordUserVerification extends AbstractTimestampEntity { @Setter private LocalDateTime expiresAt; + public Optional getStakeAddress() { + return Optional.ofNullable(stakeAddress); + } + + public void setStakeAddress(Optional stakeAddress) { + this.stakeAddress = stakeAddress.orElse(null); + } + @Override public String toString() { return "DiscordUserVerification{" + diff --git a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/jobs/CleanupAllForFinishedEventsJob.java b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/jobs/CleanupAllForFinishedEventsJob.java new file mode 100644 index 000000000..c24d60d5a --- /dev/null +++ b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/jobs/CleanupAllForFinishedEventsJob.java @@ -0,0 +1,53 @@ +package org.cardano.foundation.voting.jobs; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.cardano.foundation.voting.client.ChainFollowerClient; +import org.cardano.foundation.voting.service.discord.DiscordUserVerificationService; +import org.cardano.foundation.voting.service.sms.SMSUserVerificationService; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@RequiredArgsConstructor +public class CleanupAllForFinishedEventsJob implements Runnable { + + private final ChainFollowerClient chainFollowerClient; + + private final SMSUserVerificationService smsUserVerificationService; + + private final DiscordUserVerificationService discordUserVerificationService; + + @Scheduled(cron = "${finished.verifications.cleanup.job.cron}") + public void run() { + log.info("Cleaning up verified phones for event..."); + + var allEventsE = chainFollowerClient.findAllEvents(); + + if (allEventsE.isEmpty()) { + log.warn("No events found in ledger follower, skipping cleanup job."); + return; + } + + var allEvents = allEventsE.get(); + + allEvents.forEach(eventSummary -> { + if (eventSummary.finished()) { + log.info("Event:{} is finished, removing all sms verifications...", eventSummary.id()); + + smsUserVerificationService.findAllForEvent(eventSummary.id()).forEach(userVerification -> { + log.info("Removing historical user sms verification... since eventId:{} is finished.", eventSummary.id()); + smsUserVerificationService.removeUserVerification(userVerification); + }); + + log.info("Event:{} is finished, removing all discord verifications...", eventSummary.id()); + discordUserVerificationService.findAllForEvent(eventSummary.id()).forEach(userVerification -> { + log.info("Removing historical user discord verification... since eventId:{} is finished.", eventSummary.id()); + discordUserVerificationService.removeUserVerification(userVerification); + }); + } + }); + } + +} diff --git a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/jobs/SMSPendingVerificationPhoneCleanupJob.java b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/jobs/PendingVerificationCleanupJob.java similarity index 66% rename from backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/jobs/SMSPendingVerificationPhoneCleanupJob.java rename to backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/jobs/PendingVerificationCleanupJob.java index 10b5b051f..3ec1ebf3f 100644 --- a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/jobs/SMSPendingVerificationPhoneCleanupJob.java +++ b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/jobs/PendingVerificationCleanupJob.java @@ -2,6 +2,7 @@ import lombok.extern.slf4j.Slf4j; import org.cardano.foundation.voting.client.ChainFollowerClient; +import org.cardano.foundation.voting.service.discord.DiscordUserVerificationService; import org.cardano.foundation.voting.service.sms.SMSUserVerificationService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -13,7 +14,7 @@ @Service @Slf4j -public class SMSPendingVerificationPhoneCleanupJob implements Runnable { +public class PendingVerificationCleanupJob implements Runnable { @Autowired private ChainFollowerClient chainFollowerClient; @@ -21,6 +22,9 @@ public class SMSPendingVerificationPhoneCleanupJob implements Runnable { @Autowired private SMSUserVerificationService smsUserVerificationService; + @Autowired + private DiscordUserVerificationService discordUserVerificationService; + @Autowired private Clock clock; @@ -43,6 +47,7 @@ public void run() { allEvents.forEach(eventSummary -> { var id = eventSummary.id(); + log.info("Cleaning up pending phone verifications for event: {}", id); smsUserVerificationService.findAllPending(id).forEach(userVerification -> { var now = LocalDateTime.now(clock); @@ -56,6 +61,19 @@ public void run() { } }); + log.info("Cleaning up pending discord verifications for event: {}", id); + discordUserVerificationService.findAllPending(id).forEach(userVerification -> { + var now = LocalDateTime.now(clock); + + boolean expiredVerification = now.isAfter(userVerification.getCreatedAt() + .plusHours(pendingVerificationPhoneExpirationTimeHours)); + + if (expiredVerification) { + log.info("Deleting expired pending user verification: {}", userVerification); + discordUserVerificationService.removeUserVerification(userVerification); + } + }); + }); } diff --git a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/jobs/SMSCleanupVerifiedPhonesForEventJob.java b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/jobs/SMSCleanupVerifiedPhonesForEventJob.java deleted file mode 100644 index 6d030b159..000000000 --- a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/jobs/SMSCleanupVerifiedPhonesForEventJob.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.cardano.foundation.voting.jobs; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.cardano.foundation.voting.client.ChainFollowerClient; -import org.cardano.foundation.voting.service.sms.SMSUserVerificationService; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; - -@Service -@Slf4j -@RequiredArgsConstructor -public class SMSCleanupVerifiedPhonesForEventJob implements Runnable { - - private final ChainFollowerClient chainFollowerClient; - - private final SMSUserVerificationService smsUserVerificationService; - - @Scheduled(cron = "${finished.verifications.cleanup.job.cron}") - public void run() { - log.info("Cleaning up verified phones for event..."); - - var allEventsE = chainFollowerClient.findAllEvents(); - - if (allEventsE.isEmpty()) { - log.warn("No events found in ledger follower, skipping cleanup job."); - return; - } - - var allEvents = allEventsE.get(); - - allEvents.forEach(eventSummary -> { - smsUserVerificationService.findAllForEvent(eventSummary.id()).forEach(userVerification -> { - if (eventSummary.finished()) { - log.info("Removing historical user verification... since eventId:{} is finished.", eventSummary.id()); - smsUserVerificationService.removeUserVerification(userVerification); - } - }); - }); - } - -} - - -/// SAR (30 days dirty trick) vs DDR \ No newline at end of file diff --git a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/repository/DiscordUserVerificationRepository.java b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/repository/DiscordUserVerificationRepository.java index e9c54fcb9..f760da923 100644 --- a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/repository/DiscordUserVerificationRepository.java +++ b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/repository/DiscordUserVerificationRepository.java @@ -6,6 +6,7 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.util.List; import java.util.Optional; @Repository @@ -18,15 +19,20 @@ Optional findCompletedVerification(@Param("eventId") St @Query("SELECT uv FROM DiscordUserVerification uv WHERE uv.status = 'VERIFIED' AND uv.eventId = :eventId AND uv.discordIdHash = :discordIdHash") Optional findCompletedVerificationBasedOnDiscordUserHash(@Param("eventId") String eventId, - @Param("discordIdHash") String discordIdHash); + @Param("discordIdHash") String discordIdHash + ); @Query("SELECT uv FROM DiscordUserVerification uv WHERE uv.status = 'PENDING'" + " AND uv.eventId = :eventId" + - " AND uv.discordIdHash = :discordIdHash" + - " AND uv.secretCode = :secretCode") - Optional findPendingVerification(@Param("eventId") String eventId, - @Param("discordIdHash") String discordIdHash, - @Param("secretCode") String secretCode + " AND uv.discordIdHash = :discordIdHash") + Optional findPendingVerificationBasedOnDiscordUserHash(@Param("eventId") String eventId, + @Param("discordIdHash") String discordIdHash ); + @Query("SELECT uv FROM DiscordUserVerification uv WHERE uv.eventId = :eventId") + List findAllForEvent(String eventId); + + @Query("SELECT uv FROM DiscordUserVerification uv WHERE uv.eventId = :eventId AND uv.status = 'PENDING'") + List findAllPending(String eventId); + } diff --git a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/resource/UserVerificationResource.java b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/resource/UserVerificationResource.java index 72514293c..6afc07d73 100644 --- a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/resource/UserVerificationResource.java +++ b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/resource/UserVerificationResource.java @@ -1,16 +1,23 @@ package org.cardano.foundation.voting.resource; import io.micrometer.core.annotation.Timed; +import io.vavr.control.Either; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.cardano.foundation.voting.domain.IsVerifiedRequest; +import org.cardano.foundation.voting.domain.IsVerifiedResponse; import org.cardano.foundation.voting.service.discord.DiscordUserVerificationService; import org.cardano.foundation.voting.service.sms.SMSUserVerificationService; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.zalando.problem.Problem; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static java.util.concurrent.TimeUnit.SECONDS; import static org.springframework.web.bind.annotation.RequestMethod.GET; @RestController @@ -24,19 +31,32 @@ public class UserVerificationResource { @RequestMapping(value = "/verified/{eventId}/{stakeAddress}", method = GET, produces = "application/json") @Timed(value = "resource.isVerified", histogram = true) - public ResponseEntity isVerified(@PathVariable("eventId") String eventId, @PathVariable("stakeAddress") String stakeAddress) { + public ResponseEntity isVerified(@PathVariable("eventId") String eventId, @PathVariable("stakeAddress") String stakeAddress) throws ExecutionException, InterruptedException { var isVerifiedRequest = new IsVerifiedRequest(stakeAddress, eventId); log.info("Received isVerified request: {}", isVerifiedRequest); - // TODO fork join to sms and discord services and check is verified for both in parallel + CompletableFuture> smsVerificationFuture = CompletableFuture.supplyAsync(() -> { + return smsUserVerificationService.isVerified(isVerifiedRequest); + }); + + CompletableFuture> discordVerificationFuture = CompletableFuture.supplyAsync(() -> { + return discordUserVerificationService.isVerified(isVerifiedRequest); + }); + + var isVerified = CompletableFuture.anyOf(smsVerificationFuture, discordVerificationFuture); + + var isVerifiedResponseE = (Either) isVerified + .orTimeout(30, SECONDS) + .get(); - return smsUserVerificationService.isVerified(isVerifiedRequest) - .fold(problem -> ResponseEntity.status(problem.getStatus().getStatusCode()).body(problem), - isVerifiedResponse -> { - return ResponseEntity.ok().body(isVerifiedResponse); - } - ); + return isVerifiedResponseE.fold(problem -> { + return ResponseEntity.status(problem.getStatus().getStatusCode()).body(problem); + }, + isVerifiedResponse -> { + return ResponseEntity.ok().body(isVerifiedResponse); + } + ); } } diff --git a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/service/discord/DefaultDiscordUserVerificationService.java b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/service/discord/DefaultDiscordUserVerificationService.java index 3d7bf411c..37d73cec0 100644 --- a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/service/discord/DefaultDiscordUserVerificationService.java +++ b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/service/discord/DefaultDiscordUserVerificationService.java @@ -3,6 +3,7 @@ import io.vavr.control.Either; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.cardano.foundation.voting.client.ChainFollowerClient; import org.cardano.foundation.voting.domain.CardanoNetwork; import org.cardano.foundation.voting.domain.IsVerifiedRequest; import org.cardano.foundation.voting.domain.IsVerifiedResponse; @@ -14,6 +15,7 @@ import org.cardano.foundation.voting.utils.StakeAddress; import org.cardanofoundation.cip30.AddressFormat; import org.cardanofoundation.cip30.CIP30Verifier; +import org.cardanofoundation.cip30.MessageFormat; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -22,10 +24,11 @@ import java.time.Clock; import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; import static org.cardano.foundation.voting.domain.VerificationStatus.PENDING; import static org.cardano.foundation.voting.domain.VerificationStatus.VERIFIED; -import static org.cardanofoundation.cip30.MessageFormat.TEXT; import static org.zalando.problem.Status.BAD_REQUEST; @Service @@ -33,6 +36,9 @@ @AllArgsConstructor public class DefaultDiscordUserVerificationService implements DiscordUserVerificationService { + @Autowired + private ChainFollowerClient chainFollowerClient; + @Autowired private DiscordUserVerificationRepository userVerificationRepository; @@ -74,6 +80,37 @@ public Either startVerification(Disco .status(PENDING) .build(); + var eventDetails = chainFollowerClient.findEventById(discordBotEventIdBinding); + + if (eventDetails.isEmpty()) { + log.error("event error:{}", eventDetails.getLeft()); + + return Either.left(eventDetails.getLeft()); + } + + var maybeEvent = eventDetails.get(); + if (maybeEvent.isEmpty()) { + log.warn("Active event not found:{}", discordBotEventIdBinding); + + return Either.left(Problem.builder() + .withTitle("EVENT_NOT_FOUND") + .withDetail("Event not found, eventId:" + discordBotEventIdBinding) + .withStatus(BAD_REQUEST) + .build()); + } + + var event = maybeEvent.orElseThrow(); + + if (event.finished()) { + log.warn("Event already finished:{}", discordBotEventIdBinding); + + return Either.left(Problem.builder() + .withTitle("EVENT_ALREADY_FINISHED") + .withDetail("Event already finished, eventId:" + discordBotEventIdBinding) + .withStatus(BAD_REQUEST) + .build()); + } + var saved = userVerificationRepository.saveAndFlush(discordUserVerification); return Either.right(DiscordStartVerificationResponse.builder() @@ -87,6 +124,37 @@ public Either startVerification(Disco @Override @Transactional public Either checkVerification(DiscordCheckVerificationRequest checkVerificationRequest) { + var eventDetails = chainFollowerClient.findEventById(discordBotEventIdBinding); + + if (eventDetails.isEmpty()) { + log.error("event error:{}", eventDetails.getLeft()); + + return Either.left(eventDetails.getLeft()); + } + + var maybeEvent = eventDetails.get(); + if (maybeEvent.isEmpty()) { + log.warn("Active event not found:{}", discordBotEventIdBinding); + + return Either.left(Problem.builder() + .withTitle("EVENT_NOT_FOUND") + .withDetail("Event not found, eventId:" + discordBotEventIdBinding) + .withStatus(BAD_REQUEST) + .build()); + } + + var event = maybeEvent.orElseThrow(); + + if (event.finished()) { + log.warn("Event already finished:{}", discordBotEventIdBinding); + + return Either.left(Problem.builder() + .withTitle("EVENT_ALREADY_FINISHED") + .withDetail("Event already finished, eventId:" + discordBotEventIdBinding) + .withStatus(BAD_REQUEST) + .build()); + } + var stakeAddress = checkVerificationRequest.getStakeAddress(); var coseSignature = checkVerificationRequest.getCoseSignature(); @@ -103,7 +171,7 @@ public Either checkVerification(DiscordCheckVerific ); } - var msg = cip30VerificationResult.getMessage(TEXT); + var msg = cip30VerificationResult.getMessage(MessageFormat.TEXT); var items = msg.split("\\|"); if (items.length != 2) { @@ -149,13 +217,25 @@ public Either checkVerification(DiscordCheckVerific return Either.left(Problem.builder() .withTitle("USER_ALREADY_VERIFIED") .withDetail("User already verified.") + .with("discordIdHash", discordIdHash) .build() ); } - var maybePendingVerification = userVerificationRepository.findPendingVerification(discordBotEventIdBinding, discordIdHash, secret); + var maybePendingVerification = userVerificationRepository.findPendingVerificationBasedOnDiscordUserHash(discordBotEventIdBinding, discordIdHash); if (maybePendingVerification.isEmpty()) { + return Either.left(Problem.builder() + .withTitle("NO_PENDING_VERIFICATION") + .withDetail("No pending verification found for discordIdHash:" + discordIdHash) + .with("discordIdHash", discordIdHash) + .build() + ); + } + + boolean isSecretCodeMatch = maybePendingVerification.get().getSecretCode().equals(secret); + + if (!isSecretCodeMatch) { return Either.left(Problem.builder() .withTitle("AUTH_FAILED") .withDetail("Invalid secret and / or discordIdHash.") @@ -176,7 +256,7 @@ public Either checkVerification(DiscordCheckVerific .build()); } - pendingUserVerification.setStakeAddress(stakeAddress); + pendingUserVerification.setStakeAddress(Optional.of(stakeAddress)); pendingUserVerification.setUpdatedAt(now); pendingUserVerification.setStatus(VERIFIED); @@ -201,4 +281,16 @@ public void removeUserVerification(DiscordUserVerification userVerification) { userVerificationRepository.delete(userVerification); } + @Override + @Transactional(readOnly = true) + public List findAllForEvent(String eventId) { + return userVerificationRepository.findAllForEvent(eventId); + } + + @Override + @Transactional(readOnly = true) + public List findAllPending(String eventId) { + return userVerificationRepository.findAllPending(eventId); + } + } diff --git a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/service/discord/DiscordUserVerificationService.java b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/service/discord/DiscordUserVerificationService.java index a79b976f7..131ca4ef4 100644 --- a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/service/discord/DiscordUserVerificationService.java +++ b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/service/discord/DiscordUserVerificationService.java @@ -9,6 +9,8 @@ import org.cardano.foundation.voting.domain.entity.DiscordUserVerification; import org.zalando.problem.Problem; +import java.util.List; + public interface DiscordUserVerificationService { Either startVerification(DiscordStartVerificationRequest startVerificationRequest); @@ -19,8 +21,8 @@ public interface DiscordUserVerificationService { void removeUserVerification(DiscordUserVerification userVerification); -// List findAllForEvent(String eventId); -// -// List findAllPending(String eventId); + List findAllForEvent(String eventId); + + List findAllPending(String eventId); } diff --git a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/service/sms/DefaultSMSSMSUserVerificationService.java b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/service/sms/DefaultSMSSMSUserVerificationService.java index 6f6055128..beef3f9dd 100644 --- a/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/service/sms/DefaultSMSSMSUserVerificationService.java +++ b/backend-services/user-verification-service/src/main/java/org/cardano/foundation/voting/service/sms/DefaultSMSSMSUserVerificationService.java @@ -48,7 +48,7 @@ public class DefaultSMSSMSUserVerificationService implements SMSUserVerification private SMSService smsService; @Autowired - private SMSUserVerificationRepository SMSUserVerificationRepository; + private SMSUserVerificationRepository smsUserVerificationRepository; @Autowired private SaltHolder saltHolder; @@ -82,15 +82,15 @@ public Either startVerification(SMSStartV return Either.left(stakeAddressCheckE.getLeft()); } - var activeEventE = chainFollowerClient.findEventById(eventId); + var eventDetails = chainFollowerClient.findEventById(eventId); - if (activeEventE.isEmpty()) { - log.error("Active event error:{}", activeEventE.getLeft()); + if (eventDetails.isEmpty()) { + log.error("event error:{}", eventDetails.getLeft()); - return Either.left(activeEventE.getLeft()); + return Either.left(eventDetails.getLeft()); } - var maybeEvent = activeEventE.get(); + var maybeEvent = eventDetails.get(); if (maybeEvent.isEmpty()) { log.warn("Active event not found:{}", eventId); @@ -113,7 +113,7 @@ public Either startVerification(SMSStartV .build()); } - var maybeUserVerificationStakeAddress = SMSUserVerificationRepository.findAllCompletedPerStake( + var maybeUserVerificationStakeAddress = smsUserVerificationRepository.findAllCompletedPerStake( eventId, stakeAddress ).stream().findFirst(); @@ -147,7 +147,7 @@ public Either startVerification(SMSStartV var formattedPhoneStr = PhoneNumberUtil.getInstance().format(phoneNum, INTERNATIONAL); var phoneHash = saltedPhoneHash(formattedPhoneStr); - var maybeUserVerificationPhoneHash = SMSUserVerificationRepository.findAllCompletedPerPhone( + var maybeUserVerificationPhoneHash = smsUserVerificationRepository.findAllCompletedPerPhone( eventId, phoneHash ).stream().findFirst(); @@ -168,7 +168,7 @@ public Either startVerification(SMSStartV } } - int pendingPerStakeAddressCount = SMSUserVerificationRepository.findPendingPerStakeAddressPerPhoneCount(eventId, stakeAddress, phoneHash); + int pendingPerStakeAddressCount = smsUserVerificationRepository.findPendingPerStakeAddressPerPhoneCount(eventId, stakeAddress, phoneHash); if (pendingPerStakeAddressCount >= maxPendingVerificationAttempts) { return Either.left(Problem.builder() .withTitle("MAX_VERIFICATION_ATTEMPTS_REACHED") @@ -203,7 +203,7 @@ public Either startVerification(SMSStartV .expiresAt(now.plusMinutes(validationExpirationTimeMinutes)) .build(); - var saved = SMSUserVerificationRepository.saveAndFlush(newUserVerification); + var saved = smsUserVerificationRepository.saveAndFlush(newUserVerification); var startVerificationResponse = new SMSStartVerificationResponse( saved.getEventId(), @@ -258,7 +258,7 @@ public Either checkVerification(SMSCheckVerificatio .build()); } - var maybeUserVerification = SMSUserVerificationRepository.findAllCompletedPerStake( + var maybeUserVerification = smsUserVerificationRepository.findAllCompletedPerStake( eventId, stakeAddress ).stream().findFirst(); @@ -277,7 +277,7 @@ public Either checkVerification(SMSCheckVerificatio } } - var maybePendingRequest = SMSUserVerificationRepository.findPendingVerificationsByEventIdAndStakeAddressAndRequestId( + var maybePendingRequest = smsUserVerificationRepository.findPendingVerificationsByEventIdAndStakeAddressAndRequestId( eventId, stakeAddress, checkVerificationRequest.getRequestId() @@ -326,7 +326,7 @@ public Either checkVerification(SMSCheckVerificatio pendingUserVerification.setStatus(VERIFIED); pendingUserVerification.setUpdatedAt(now); - var saved = SMSUserVerificationRepository.saveAndFlush(pendingUserVerification); + var saved = smsUserVerificationRepository.saveAndFlush(pendingUserVerification); return Either.right(new IsVerifiedResponse(saved.getStatus() == VERIFIED)); } @@ -365,7 +365,7 @@ public Either isVerified(IsVerifiedRequest isVerifi .build()); } - var maybeUserVerification = SMSUserVerificationRepository.findAllCompletedPerStake( + var maybeUserVerification = smsUserVerificationRepository.findAllCompletedPerStake( isVerifiedRequest.getEventId(), isVerifiedRequest.getStakeAddress() ).stream().findFirst(); @@ -387,19 +387,19 @@ public Either isVerified(IsVerifiedRequest isVerifi @Override @Transactional public void removeUserVerification(SMSUserVerification SMSUserVerification) { - SMSUserVerificationRepository.delete(SMSUserVerification); + smsUserVerificationRepository.delete(SMSUserVerification); } @Override @Transactional(readOnly = true) public List findAllForEvent(String eventId) { - return SMSUserVerificationRepository.findAllByEventId(eventId); + return smsUserVerificationRepository.findAllByEventId(eventId); } @Override @Transactional(readOnly = true) public List findAllPending(String eventId) { - return SMSUserVerificationRepository.findAllPending(eventId); + return smsUserVerificationRepository.findAllPending(eventId); } private static Optional isValidNumber(String userEnteredPhoneNumber) { diff --git a/backend-services/user-verification-service/src/main/resources/application.properties b/backend-services/user-verification-service/src/main/resources/application.properties index dd18ddcd1..3714b383f 100644 --- a/backend-services/user-verification-service/src/main/resources/application.properties +++ b/backend-services/user-verification-service/src/main/resources/application.properties @@ -67,5 +67,7 @@ spring.h2.console.enabled=${H2_CONSOLE_ENABLED:true} phone.number.salt=${SALT:67274569c9671a4ae3f753b9647ca719} discord.bot.eventId.binding=${DISCORD_BOT_EVENT_ID_BINDING:CF_SUMMIT_2023_3A33} +spring.task.scheduling.pool.size=${SCHEDULING_POOL_SIZE:5} + spring.jackson.default-property-inclusion=non_null diff --git a/backend-services/user-verification-service/src/main/resources/db/migration/h2/V0__user_verification_service_init.sql b/backend-services/user-verification-service/src/main/resources/db/migration/h2/V0__user_verification_service_init.sql index fe107bed4..3191f8bb2 100644 --- a/backend-services/user-verification-service/src/main/resources/db/migration/h2/V0__user_verification_service_init.sql +++ b/backend-services/user-verification-service/src/main/resources/db/migration/h2/V0__user_verification_service_init.sql @@ -11,40 +11,49 @@ CREATE TABLE sms_user_verification ( status VARCHAR(255) NOT NULL, + expires_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - expires_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - - CONSTRAINT pk_user PRIMARY KEY (id) + CONSTRAINT pk_sms_user PRIMARY KEY (id) ); +create index idx_sms_event on sms_user_verification(event_id); + +create index idx_sms_status on sms_user_verification(event_id, status); + +create index idx_sms_stake_address_status on sms_user_verification(event_id, stake_address, status); + +create index idx_sms_status_phone_hash on sms_user_verification(event_id, status, phone_number_hash); + +create index idx_sms_stake_address_status_phone_hash on sms_user_verification(event_id, stake_address, status, phone_number_hash); + +create index idx_sms_stake_address_status_req_id on sms_user_verification(event_id, stake_address, status, request_id); + CREATE TABLE discord_user_verification ( - hashed_discord_id VARCHAR(255) NOT NULL, + discord_id_hash VARCHAR(255) NOT NULL, - stake_address VARCHAR(255) NOT NULL, event_id VARCHAR(255) NOT NULL, - verification_code VARCHAR(255) NOT NULL, + stake_address VARCHAR(255), + + secret_code VARCHAR(255) NOT NULL, status VARCHAR(255) NOT NULL, + expires_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - expires_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - - CONSTRAINT pk_user PRIMARY KEY (id) + CONSTRAINT pk_discord_id_hash PRIMARY KEY (discord_id_hash) ); -create index idx_sms_event on sms_user_verification(event_id); +create index idx_discord_stake_event_id on sms_user_verification(event_id); -create index idx_sms_status on sms_user_verification(event_id, status); - -create index idx_sms_stake_address_status on sms_user_verification(event_id, stake_address, status); +create index idx_discord_event_id_status on sms_user_verification(event_id, status); -create index idx_sms_status_phone_hash on sms_user_verification(event_id, status, phone_number_hash); +create index idx_discord_stake_address_status on sms_user_verification(event_id, stake_address, status); -create index idx_sms_stake_address_status_phone_hash on sms_user_verification(event_id, stake_address, status, phone_number_hash); - -create index idx_sms_stake_address_status_req_id on sms_user_verification(event_id, stake_address, status, request_id); +create index idx_discord_status_event_discord_id_hash on sms_user_verification(event_id, status, discord_id_hash); diff --git a/backend-services/user-verification-service/src/main/resources/db/migration/postgresql/V0__user_verification_service_init.sql b/backend-services/user-verification-service/src/main/resources/db/migration/postgresql/V0__user_verification_service_init.sql index fe107bed4..3191f8bb2 100644 --- a/backend-services/user-verification-service/src/main/resources/db/migration/postgresql/V0__user_verification_service_init.sql +++ b/backend-services/user-verification-service/src/main/resources/db/migration/postgresql/V0__user_verification_service_init.sql @@ -11,40 +11,49 @@ CREATE TABLE sms_user_verification ( status VARCHAR(255) NOT NULL, + expires_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - expires_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - - CONSTRAINT pk_user PRIMARY KEY (id) + CONSTRAINT pk_sms_user PRIMARY KEY (id) ); +create index idx_sms_event on sms_user_verification(event_id); + +create index idx_sms_status on sms_user_verification(event_id, status); + +create index idx_sms_stake_address_status on sms_user_verification(event_id, stake_address, status); + +create index idx_sms_status_phone_hash on sms_user_verification(event_id, status, phone_number_hash); + +create index idx_sms_stake_address_status_phone_hash on sms_user_verification(event_id, stake_address, status, phone_number_hash); + +create index idx_sms_stake_address_status_req_id on sms_user_verification(event_id, stake_address, status, request_id); + CREATE TABLE discord_user_verification ( - hashed_discord_id VARCHAR(255) NOT NULL, + discord_id_hash VARCHAR(255) NOT NULL, - stake_address VARCHAR(255) NOT NULL, event_id VARCHAR(255) NOT NULL, - verification_code VARCHAR(255) NOT NULL, + stake_address VARCHAR(255), + + secret_code VARCHAR(255) NOT NULL, status VARCHAR(255) NOT NULL, + expires_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - expires_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - - CONSTRAINT pk_user PRIMARY KEY (id) + CONSTRAINT pk_discord_id_hash PRIMARY KEY (discord_id_hash) ); -create index idx_sms_event on sms_user_verification(event_id); +create index idx_discord_stake_event_id on sms_user_verification(event_id); -create index idx_sms_status on sms_user_verification(event_id, status); - -create index idx_sms_stake_address_status on sms_user_verification(event_id, stake_address, status); +create index idx_discord_event_id_status on sms_user_verification(event_id, status); -create index idx_sms_status_phone_hash on sms_user_verification(event_id, status, phone_number_hash); +create index idx_discord_stake_address_status on sms_user_verification(event_id, stake_address, status); -create index idx_sms_stake_address_status_phone_hash on sms_user_verification(event_id, stake_address, status, phone_number_hash); - -create index idx_sms_stake_address_status_req_id on sms_user_verification(event_id, stake_address, status, request_id); +create index idx_discord_status_event_discord_id_hash on sms_user_verification(event_id, status, discord_id_hash);