Modify POST /user/:id/setupTotp to work with adjusted requirements

This commit is contained in:
Colin McDonald 2016-06-24 18:55:47 -04:00
parent aa527fa567
commit aaba9cb369
5 changed files with 29 additions and 33 deletions

View File

@ -109,7 +109,7 @@
<version>2.7.0</version> <version>2.7.0</version>
</dependency> </dependency>
<!-- TOTP --> <!-- Totp -->
<dependency> <dependency>
<groupId>com.warrenstrange</groupId> <groupId>com.warrenstrange</groupId>
<artifactId>googleauth</artifactId> <artifactId>googleauth</artifactId>

View File

@ -25,6 +25,7 @@ import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions; import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.net.JksOptions;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.BodyHandler;
@ -218,7 +219,8 @@ public final class APIv3 extends AbstractVerticle {
private void setupHttpServer() { private void setupHttpServer() {
HttpServer webServer = vertx.createHttpServer( HttpServer webServer = vertx.createHttpServer(
new HttpServerOptions() new HttpServerOptions()
//.setSsl(true) // TODO //.setSsl(true)
//.setKeyStoreOptions(new JksOptions().setPath("c:\\Users\\cmcdonald\\Desktop\\apiv3.jks").setPassword("password"))
.setCompressionSupported(true) .setCompressionSupported(true)
); );

View File

@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableMap;
import com.mongodb.client.result.UpdateResult; import com.mongodb.client.result.UpdateResult;
import com.warrenstrange.googleauth.GoogleAuthenticatorKey; import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.model.User;
@ -24,20 +25,27 @@ public final class POSTUserSetupTotp implements Handler<RoutingContext> {
} }
if (user.getTotpSecret() != null) { if (user.getTotpSecret() != null) {
ErrorUtils.respondInvalidInput(ctx, "User provided already has TOTP code set."); ErrorUtils.respondInvalidInput(ctx, "User provided already has totp setup.");
return; return;
} }
GoogleAuthenticatorKey generated = TotpUtils.generateTotpSecret(); JsonObject requestBody = ctx.getBodyAsJson();
String secret = requestBody.getString("secret");
int code = requestBody.getInteger("code");
user.setTotpSecret(generated.getKey()); if (TotpUtils.authorizeUser(secret, code)) {
BlockingCallback<UpdateResult> callback = new BlockingCallback<>(); user.setTotpSecret(secret);
user.save(callback); BlockingCallback<UpdateResult> callback = new BlockingCallback<>();
callback.get(); user.save(callback);
callback.get();
APIv3.respondJson(ctx, ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"qrCode", TotpUtils.getQrCodeUrl(user, generated) "success", true,
)); "message", "Totp code set."
));
} else {
ErrorUtils.respondInvalidInput(ctx, "Confirmation code provided did not match.");
}
} }
} }

View File

@ -26,7 +26,7 @@ public final class POSTUserVerifyTotp implements Handler<RoutingContext> {
} }
if (user.getTotpSecret() == null) { if (user.getTotpSecret() == null) {
ErrorUtils.respondInvalidInput(ctx, "User provided does not have TOTP code set."); ErrorUtils.respondInvalidInput(ctx, "User provided does not have totp code set.");
return; return;
} }
@ -45,7 +45,7 @@ public final class POSTUserVerifyTotp implements Handler<RoutingContext> {
if (recentlyUsedCallback.get()) { if (recentlyUsedCallback.get()) {
APIv3.respondJson(ctx, ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"authorized", false, "authorized", false,
"message", "TOTP code was recently used." "message", "Totp code was recently used."
)); ));
return; return;
} }
@ -63,12 +63,12 @@ public final class POSTUserVerifyTotp implements Handler<RoutingContext> {
APIv3.respondJson(ctx, ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"authorized", true, "authorized", true,
"message", "Valid TOTP code provided." "message", "Valid totp code provided."
)); ));
} else { } else {
APIv3.respondJson(ctx, ImmutableMap.of( APIv3.respondJson(ctx, ImmutableMap.of(
"authorized", false, "authorized", false,
"message", "TOTP code was not valid." "message", "Totp code was not valid."
)); ));
} }
} }

View File

@ -3,8 +3,6 @@ package net.frozenorb.apiv3.util;
import com.mongodb.async.SingleResultCallback; import com.mongodb.async.SingleResultCallback;
import com.warrenstrange.googleauth.GoogleAuthenticator; import com.warrenstrange.googleauth.GoogleAuthenticator;
import com.warrenstrange.googleauth.GoogleAuthenticatorConfig; import com.warrenstrange.googleauth.GoogleAuthenticatorConfig;
import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator;
import io.vertx.redis.RedisClient; import io.vertx.redis.RedisClient;
import io.vertx.redis.RedisOptions; import io.vertx.redis.RedisOptions;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
@ -23,20 +21,8 @@ public class TotpUtils {
.setPort(Integer.parseInt(APIv3.getConfig().getProperty("redis.port"))) .setPort(Integer.parseInt(APIv3.getConfig().getProperty("redis.port")))
); );
public static GoogleAuthenticatorKey generateTotpSecret() { public static boolean authorizeUser(String secret, int code) {
return googleAuthenticator.createCredentials(); return googleAuthenticator.authorize(secret, code);
}
public static boolean authorizeUser(User user, int code) {
return googleAuthenticator.authorize(user.getTotpSecret(), code);
}
public static String getQrCodeUrl(User user, GoogleAuthenticatorKey secret) {
return GoogleAuthenticatorQRGenerator.getOtpAuthURL(
"MineHQ Network",
user.getLastUsername(),
secret
);
} }
public static void isPreAuthorized(User user, String ip, SingleResultCallback<Boolean> callback) { public static void isPreAuthorized(User user, String ip, SingleResultCallback<Boolean> callback) {
@ -68,7 +54,7 @@ public class TotpUtils {
} }
public static void wasRecentlyUsed(User user, int code, SingleResultCallback<Boolean> callback) { public static void wasRecentlyUsed(User user, int code, SingleResultCallback<Boolean> callback) {
redisClient.exists(user.getId() + ":recentTOTPCodes:" + code, (result) -> { redisClient.exists(user.getId() + ":recentTotpCodes:" + code, (result) -> {
if (result.succeeded()) { if (result.succeeded()) {
callback.onResult(result.result() == 1 , null); callback.onResult(result.result() == 1 , null);
} else { } else {
@ -78,7 +64,7 @@ public class TotpUtils {
} }
public static void markRecentlyUsed(User user, int code, SingleResultCallback<Void> callback) { public static void markRecentlyUsed(User user, int code, SingleResultCallback<Void> callback) {
String key = user.getId() + ":recentTOTPCodes:" + code; String key = user.getId() + ":recentTotpCodes:" + code;
redisClient.set(key, "", (result) -> { redisClient.set(key, "", (result) -> {
if (result.succeeded()) { if (result.succeeded()) {