Finish Zang telephone linking (hopefully!). Completes #27

This commit is contained in:
Colin McDonald 2016-07-10 14:17:18 -04:00
parent 38d356f7b5
commit f84f8635a2
3 changed files with 31 additions and 6 deletions

View File

@ -60,6 +60,8 @@ public final class User {
@Getter @ExcludeFromReplies private String pendingPhone; @Getter @ExcludeFromReplies private String pendingPhone;
@Getter @ExcludeFromReplies private String pendingPhoneToken; @Getter @ExcludeFromReplies private String pendingPhoneToken;
@Getter @ExcludeFromReplies private Instant pendingPhoneTokenSetAt; @Getter @ExcludeFromReplies private Instant pendingPhoneTokenSetAt;
@Getter @ExcludeFromReplies private Instant pendingPhoneTokenVerificationAttemptedAt;
@Getter @ExcludeFromReplies private Set<Instant> phoneVerificationFailedAttempts;
@Getter private String lastSeenOn; @Getter private String lastSeenOn;
@Getter private Instant lastSeenAt; @Getter private Instant lastSeenAt;
@Getter private Instant firstSeenAt; @Getter private Instant firstSeenAt;
@ -503,12 +505,19 @@ public final class User {
this.pendingPhoneTokenSetAt = Instant.now(); this.pendingPhoneTokenSetAt = Instant.now();
} }
public void failedPhoneRegistration() {
this.pendingPhoneTokenVerificationAttemptedAt = Instant.now();
this.phoneVerificationFailedAttempts.add(Instant.now());
}
public void completePhoneRegistration(String phoneNumber) { public void completePhoneRegistration(String phoneNumber) {
this.phone = phoneNumber; this.phone = phoneNumber;
this.phoneRegisteredAt = Instant.now(); this.phoneRegisteredAt = Instant.now();
this.pendingPhone = null; this.pendingPhone = null;
this.pendingPhoneToken = null; this.pendingPhoneToken = null;
this.pendingPhoneTokenSetAt = null; this.pendingPhoneTokenSetAt = null;
this.pendingPhoneTokenVerificationAttemptedAt = null;
this.phoneVerificationFailedAttempts = null;
} }
public void hasPermissionAnywhere(String permission, SingleResultCallback<Boolean> callback) { public void hasPermissionAnywhere(String permission, SingleResultCallback<Boolean> callback) {

View File

@ -12,6 +12,7 @@ import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.ErrorUtils;
import java.time.Instant;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public final class POSTUsersIdConfirmPhone implements Handler<RoutingContext> { public final class POSTUsersIdConfirmPhone implements Handler<RoutingContext> {
@ -27,25 +28,40 @@ public final class POSTUsersIdConfirmPhone implements Handler<RoutingContext> {
} }
if (user.getPhone() != null) { if (user.getPhone() != null) {
ErrorUtils.respondInvalidInput(ctx, "User provided already has a confirmed phone number."); ErrorUtils.respondOther(ctx, 409, "User provided already has a confirmed phone number", "phoneNumberAlreadyConfirmed", ImmutableMap.of());
return; return;
} }
if (user.getPendingPhoneToken() == null) { if (user.getPendingPhoneToken() == null) {
ErrorUtils.respondInvalidInput(ctx, "User provided already hasn't started confirming a phone number."); ErrorUtils.respondOther(ctx, 409, "User provided already hasn't started confirming a phone number.", "phoneConfirmationNotStarted", ImmutableMap.of());
return; return;
} }
JsonObject requestBody = ctx.getBodyAsJson(); JsonObject requestBody = ctx.getBodyAsJson();
int phoneCode = requestBody.getInteger("phoneCode"); int phoneCode = requestBody.getInteger("phoneCode");
if ((System.currentTimeMillis() - user.getPendingPhoneTokenSetAt().toEpochMilli()) > TimeUnit.MINUTES.toMillis(20)) { if ((System.currentTimeMillis() - user.getPendingPhoneTokenSetAt().toEpochMilli()) > TimeUnit.HOURS.toMillis(6)) {
ErrorUtils.respondInvalidInput(ctx, "Phone token is expired"); ErrorUtils.respondOther(ctx, 409, "Phone token is expired", "phoneTokenExpired", ImmutableMap.of());
return;
}
if ((System.currentTimeMillis() - user.getPendingPhoneTokenVerificationAttemptedAt().toEpochMilli()) < TimeUnit.MINUTES.toMillis(20)) {
ErrorUtils.respondOther(ctx, 409, "Wait before attempting phone verification again.", "waitBeforeAttemptingPhoneVerificationAgain", ImmutableMap.of());
return;
}
if (user.getPhoneVerificationFailedAttempts().size() >= 5) {
ErrorUtils.respondOther(ctx, 409, "Too many failed verification attempts", "tooManyFailedPhoneVerifications", ImmutableMap.of());
return; return;
} }
if (!String.valueOf(phoneCode).equals(user.getPendingPhoneToken())) { if (!String.valueOf(phoneCode).equals(user.getPendingPhoneToken())) {
ErrorUtils.respondInvalidInput(ctx, "Phone token doesn't match"); user.failedPhoneRegistration();
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
user.save(callback);
callback.get();
ErrorUtils.respondOther(ctx, 409, "Phone token doesn't match", "phoneTokenNoMatch", ImmutableMap.of());
return; return;
} }

View File

@ -45,7 +45,7 @@ public final class POSTUsersIdRegisterPhone implements Handler<RoutingContext> {
return; return;
} }
if (user.getPendingPhoneToken() != null && (System.currentTimeMillis() - user.getPendingPhoneTokenSetAt().toEpochMilli()) < TimeUnit.MINUTES.toMillis(20)) { if (user.getPendingPhoneToken() != null && (System.currentTimeMillis() - user.getPendingPhoneTokenSetAt().toEpochMilli()) < TimeUnit.HOURS.toMillis(6)) {
ErrorUtils.respondOther(ctx, 409, "Confirmation code recently sent.", "confirmationCodeRecentlySent", ImmutableMap.of()); ErrorUtils.respondOther(ctx, 409, "Confirmation code recently sent.", "confirmationCodeRecentlySent", ImmutableMap.of());
return; return;
} }