Even more Zang telephone work!

This commit is contained in:
Colin McDonald 2016-06-27 23:01:12 -04:00
parent 822dd27536
commit c8c1ac153d
7 changed files with 193 additions and 13 deletions

View File

@ -71,6 +71,7 @@ import net.frozenorb.apiv3.route.notificationTemplates.DELETENotificationTemplat
import net.frozenorb.apiv3.route.notificationTemplates.GETNotificationTemplates; import net.frozenorb.apiv3.route.notificationTemplates.GETNotificationTemplates;
import net.frozenorb.apiv3.route.notificationTemplates.GETNotificationTemplatesId; import net.frozenorb.apiv3.route.notificationTemplates.GETNotificationTemplatesId;
import net.frozenorb.apiv3.route.notificationTemplates.POSTNotificationTemplates; import net.frozenorb.apiv3.route.notificationTemplates.POSTNotificationTemplates;
import net.frozenorb.apiv3.route.phoneIntel.GETPhoneInteld;
import net.frozenorb.apiv3.route.punishments.*; import net.frozenorb.apiv3.route.punishments.*;
import net.frozenorb.apiv3.route.ranks.DELETERanksId; import net.frozenorb.apiv3.route.ranks.DELETERanksId;
import net.frozenorb.apiv3.route.ranks.GETRanks; import net.frozenorb.apiv3.route.ranks.GETRanks;
@ -299,6 +300,8 @@ public final class APIv3 extends AbstractVerticle {
//http.put("/notificationTemplates/:id").blockingHandler(new PUTNotificationTemplatesId(), false); //http.put("/notificationTemplates/:id").blockingHandler(new PUTNotificationTemplatesId(), false);
http.delete("/notificationTemplates/:id").blockingHandler(new DELETENotificationTemplatesId(), false); http.delete("/notificationTemplates/:id").blockingHandler(new DELETENotificationTemplatesId(), false);
http.get("/phoneIntel/:id").handler(new GETPhoneInteld());
http.get("/punishments/:id").handler(new GETPunishmentsId()); http.get("/punishments/:id").handler(new GETPunishmentsId());
http.get("/punishments").handler(new GETPunishments()); http.get("/punishments").handler(new GETPunishments());
http.post("/punishments").blockingHandler(new POSTPunishments(), false); http.post("/punishments").blockingHandler(new POSTPunishments(), false);

View File

@ -0,0 +1,70 @@
package net.frozenorb.apiv3.model;
import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.MongoCollection;
import fr.javatic.mongo.jacksonCodec.Entity;
import fr.javatic.mongo.jacksonCodec.objectId.Id;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.util.ZangUtils;
import net.frozenorb.apiv3.zang.ZangResult;
import org.bson.Document;
import java.time.Instant;
import java.util.LinkedList;
import java.util.List;
@Entity
@AllArgsConstructor
public final class PhoneIntel {
private static final MongoCollection<PhoneIntel> phoneIntelCollection = APIv3.getDatabase().getCollection("phoneIntel", PhoneIntel.class);
@Getter @Id private String id;
@Getter private Instant lastUpdatedAt;
@Getter private ZangResult result;
public static void findAll(SingleResultCallback<List<PhoneIntel>> callback) {
phoneIntelCollection.find().sort(new Document("lastSeenAt", -1)).into(new LinkedList<>(), callback);
}
public static void findById(String id, SingleResultCallback<PhoneIntel> callback) {
phoneIntelCollection.find(new Document("_id", id)).first(callback);
}
public static void findOrCreateById(String id, SingleResultCallback<PhoneIntel> callback) {
findById(id, (existingPhoneIntel, error) -> {
if (error != null) {
callback.onResult(null, error);
} else if (existingPhoneIntel != null) {
callback.onResult(existingPhoneIntel, null);
} else {
ZangUtils.getCarrierInfo(id, (zangResult, error2) -> {
if (error2 != null) {
callback.onResult(null, error2);
} else {
PhoneIntel newPhoneIntel = new PhoneIntel(id, zangResult);
phoneIntelCollection.insertOne(newPhoneIntel, (ignored, error3) -> {
if (error3 != null) {
callback.onResult(null, error3);
} else {
callback.onResult(newPhoneIntel, null);
}
});
}
});
}
});
}
private PhoneIntel() {} // For Jackson
private PhoneIntel(String phoneNumber, ZangResult result) {
this.id = phoneNumber;
this.lastUpdatedAt = Instant.now();
this.result = result;
}
}

View File

@ -486,16 +486,16 @@ public final class User {
public void startPhoneRegistration(String phoneNumber) { public void startPhoneRegistration(String phoneNumber) {
this.pendingPhone = phoneNumber; this.pendingPhone = phoneNumber;
this.pendingEmailToken = UUID.randomUUID().toString().replace("-", ""); this.pendingPhoneToken = String.valueOf(new Random().nextInt(999999 - 100000) + 100000);
this.pendingEmailTokenSetAt = Instant.now(); this.pendingPhoneTokenSetAt = Instant.now();
} }
public void completeRegistration(String email) { public void completePhoneRegistration(String phoneNumber) {
this.email = email; this.phone = phoneNumber;
this.registeredAt = Instant.now(); this.phoneRegisteredAt = Instant.now();
this.pendingEmail = null; this.pendingPhone = null;
this.pendingEmailToken = null; this.pendingPhoneToken = null;
this.pendingEmailTokenSetAt = null; this.pendingPhoneTokenSetAt = null;
} }
public void hasPermissionAnywhere(String permission, SingleResultCallback<Boolean> callback) { public void hasPermissionAnywhere(String permission, SingleResultCallback<Boolean> callback) {

View File

@ -26,9 +26,7 @@ public final class POSTEmailTokensIdConfirm implements Handler<RoutingContext> {
return; return;
} }
// We can't check email != null as that's set while we're pending if (user.getEmail() != null) {
// confirmation, we have to check the token.
if (user.getPendingEmailToken() == null) {
ErrorUtils.respondGeneric(ctx, 400, "User provided already has email set."); ErrorUtils.respondGeneric(ctx, 400, "User provided already has email set.");
return; return;
} }

View File

@ -0,0 +1,29 @@
package net.frozenorb.apiv3.route.phoneIntel;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.PhoneIntel;
import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.PhoneUtils;
public final class GETPhoneInteld implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
String phoneNumber = ctx.request().getParam("id");
if (!PhoneUtils.isValidPhone(phoneNumber)) {
ErrorUtils.respondInvalidInput(ctx, "Phone number \"" + phoneNumber + "\" is not valid.");
return;
}
PhoneIntel.findOrCreateById(phoneNumber, (ipIntel, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, ipIntel);
}
});
}
}

View File

@ -0,0 +1,68 @@
package net.frozenorb.apiv3.route.users;
import com.google.common.collect.ImmutableMap;
import com.mongodb.client.result.UpdateResult;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.auditLog.AuditLog;
import net.frozenorb.apiv3.auditLog.AuditLogActionType;
import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.util.ErrorUtils;
import java.util.concurrent.TimeUnit;
public final class POSTUsersIdConfirmPhone implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) {
BlockingCallback<User> userCallback = new BlockingCallback<>();
User.findById(ctx.request().getParam("id"), userCallback);
User user = userCallback.get();
if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
return;
}
if (user.getPhone() != null) {
ErrorUtils.respondInvalidInput(ctx, "User provided already has a confirmed phone number.");
return;
}
if (user.getPendingPhoneToken() == null) {
ErrorUtils.respondInvalidInput(ctx, "User provided already hasn't started confirming a phone number.");
return;
}
JsonObject requestBody = ctx.getBodyAsJson();
int phoneCode = requestBody.getInteger("phoneCode");
if ((System.currentTimeMillis() - user.getPendingPhoneTokenSetAt().toEpochMilli()) > TimeUnit.MINUTES.toMillis(20)) {
ErrorUtils.respondInvalidInput(ctx, "Phone token is expired");
return;
}
if (!String.valueOf(phoneCode).equals(user.getPendingPhoneToken())) {
ErrorUtils.respondInvalidInput(ctx, "Phone token doesn't match");
return;
}
user.completePhoneRegistration(user.getPendingPhone());
BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
user.save(callback);
callback.get();
AuditLog.log(user.getId(), requestBody.getString("userIp"), ctx, AuditLogActionType.USER_CONFIRM_PHONE, (ignored, error) -> {
if (error != null) {
ErrorUtils.respondInternalError(ctx, error);
} else {
APIv3.respondJson(ctx, ImmutableMap.of(
"success", true
));
}
});
}
}

View File

@ -8,12 +8,15 @@ import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLog;
import net.frozenorb.apiv3.auditLog.AuditLogActionType; import net.frozenorb.apiv3.auditLog.AuditLogActionType;
import net.frozenorb.apiv3.model.BannedCellCarrier;
import net.frozenorb.apiv3.model.NotificationTemplate; import net.frozenorb.apiv3.model.NotificationTemplate;
import net.frozenorb.apiv3.model.PhoneIntel;
import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.unsorted.BlockingCallback;
import net.frozenorb.apiv3.unsorted.Notification; import net.frozenorb.apiv3.unsorted.Notification;
import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.ErrorUtils;
import net.frozenorb.apiv3.util.PhoneUtils; import net.frozenorb.apiv3.util.PhoneUtils;
import net.frozenorb.apiv3.zang.ZangResult;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -43,8 +46,8 @@ public final class POSTUsersIdRegisterPhone implements Handler<RoutingContext> {
return; return;
} }
if (user.getPendingPhoneToken() != null && (System.currentTimeMillis() - user.getPendingPhoneTokenSetAt().toEpochMilli()) < TimeUnit.DAYS.toMillis(2)) { if (user.getPendingPhoneToken() != null && (System.currentTimeMillis() - user.getPendingPhoneTokenSetAt().toEpochMilli()) < TimeUnit.MINUTES.toMillis(20)) {
ErrorUtils.respondGeneric(ctx, 200, "We just recently sent you a confirmation code. Please wait before trying to register again."); ErrorUtils.respondGeneric(ctx, 200, "We just recently sent you a confirmation code. Please wait before trying to register your phone again.");
return; return;
} }
@ -56,6 +59,15 @@ public final class POSTUsersIdRegisterPhone implements Handler<RoutingContext> {
return; return;
} }
BlockingCallback<PhoneIntel> phoneIntelCallback = new BlockingCallback<>();
PhoneIntel.findOrCreateById(phone, phoneIntelCallback);
PhoneIntel phoneIntel = phoneIntelCallback.get();
if (BannedCellCarrier.findById(phoneIntel.getResult().getCarrierId()) != null) {
ErrorUtils.respondInvalidInput(ctx, phone + " is from a banned cell provider.");
return;
}
user.startPhoneRegistration(phone); user.startPhoneRegistration(phone);
BlockingCallback<UpdateResult> callback = new BlockingCallback<>(); BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
user.save(callback); user.save(callback);