diff --git a/apiv3.properties b/apiv3.properties
index 1624d10..70d0854 100644
--- a/apiv3.properties
+++ b/apiv3.properties
@@ -3,6 +3,8 @@ mongo.port=55505
mongo.database=minehqapi
mongo.username=test
mongo.password=test
+redis.address=localhost
+redis.port=6379
http.address=
http.port=80
http.workerThreads=6
diff --git a/pom.xml b/pom.xml
index c2a3a58..e6a8c37 100644
--- a/pom.xml
+++ b/pom.xml
@@ -77,6 +77,11 @@
metrics-core
3.1.2
+
+ com.bugsnag
+ bugsnag
+ 2.0.0
+
org.mongodb
mongo-java-driver
@@ -87,6 +92,11 @@
mandrillClient
1.1
+
+ com.twilio.sdk
+ twilio-java-sdk
+ 6.3.0
+
org.mindrot
jbcrypt
diff --git a/src/main/java/net/frozenorb/apiv3/APIv3.java b/src/main/java/net/frozenorb/apiv3/APIv3.java
index 0bbcfee..fad0cfd 100644
--- a/src/main/java/net/frozenorb/apiv3/APIv3.java
+++ b/src/main/java/net/frozenorb/apiv3/APIv3.java
@@ -44,6 +44,7 @@ import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Morphia;
import org.mongodb.morphia.logging.MorphiaLoggerFactory;
import org.mongodb.morphia.logging.slf4j.SLF4JLoggerImplFactory;
+import redis.clients.jedis.JedisPool;
import java.io.FileInputStream;
import java.io.InputStream;
@@ -55,6 +56,7 @@ public final class APIv3 {
@Getter private static Datastore datastore;
@Getter private static Properties config = new Properties();
+ @Getter private static JedisPool redisPool;
@Getter private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(ObjectId.class, new ObjectIdTypeAdapter())
.setExclusionStrategies(new FollowAnnotationExclusionStrategy())
@@ -65,6 +67,7 @@ public final class APIv3 {
setupConfig();
setupDatabase();
+ setupRedis();
setupHttp();
}
@@ -97,6 +100,13 @@ public final class APIv3 {
datastore.ensureIndexes();
}
+ private void setupRedis() {
+ redisPool = new JedisPool(
+ config.getProperty("redis.address"),
+ Integer.parseInt(config.getProperty("redis.port"))
+ );
+ }
+
private void setupHttp() {
ipAddress(config.getProperty("http.address"));
port(Integer.parseInt(config.getProperty("http.port")));
@@ -106,6 +116,8 @@ public final class APIv3 {
before(new AuthorizationFilter());
exception(Exception.class, new LoggingExceptionHandler());
+ // TODO: The commented out routes
+
get("/announcements", new GETAnnouncements(), gson::toJson);
get("/auditLog", new GETAuditLog(), gson::toJson);
get("/chatFilterList", new GETChatFilterList(), gson::toJson);
@@ -151,8 +163,9 @@ public final class APIv3 {
get("/user/:id/meta/:serverGroup", new GETUserMeta(), gson::toJson);
get("/user/:id/grants", new GETUserGrants(), gson::toJson);
get("/user/:id/ipLog", new GETUserIPLog(), gson::toJson);
- post("/user/:id/verifyTOTP", new POSTUserVerifyTOTP(), gson::toJson);
+ get("/user/:id/requiresTOTP", new GETUserRequiresTOTP(), gson::toJson);
get("/user/:id", new GETUser(), gson::toJson);
+ post("/user/:id/verifyTOTP", new POSTUserVerifyTOTP(), gson::toJson);
post("/user/:id:/grant", new POSTUserGrant(), gson::toJson);
post("/user/:id:/punish", new POSTUserPunish(), gson::toJson);
post("/user/:id/login", new POSTUserLogin(), gson::toJson);
diff --git a/src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java b/src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java
index 6f66ac2..86f0712 100644
--- a/src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java
+++ b/src/main/java/net/frozenorb/apiv3/filters/ActorAttributeFilter.java
@@ -26,6 +26,7 @@ public final class ActorAttributeFilter implements Filter {
}
}
+ @SuppressWarnings("deprecation") // We purposely get the User by their last username.
private Actor processBasicAuthorization(String authHeader) {
String encodedHeader = authHeader.substring("Basic ".length());
String[] credentials = Base64.base64Decode(encodedHeader).split(":");
diff --git a/src/main/java/net/frozenorb/apiv3/routes/auditLog/GETAuditLog.java b/src/main/java/net/frozenorb/apiv3/routes/auditLog/GETAuditLog.java
index cfa987a..4b46517 100644
--- a/src/main/java/net/frozenorb/apiv3/routes/auditLog/GETAuditLog.java
+++ b/src/main/java/net/frozenorb/apiv3/routes/auditLog/GETAuditLog.java
@@ -2,6 +2,7 @@ package net.frozenorb.apiv3.routes.auditLog;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.AuditLogEntry;
+import net.frozenorb.apiv3.utils.ErrorUtils;
import spark.Request;
import spark.Response;
import spark.Route;
@@ -9,10 +10,14 @@ import spark.Route;
public final class GETAuditLog implements Route {
public Object handle(Request req, Response res) {
- int limit = req.queryParams("limit") == null ? 100 : Integer.parseInt(req.queryParams("limit"));
- int offset = req.queryParams("offset") == null ? 0 : Integer.parseInt(req.queryParams("offset"));
+ try {
+ int limit = req.queryParams("limit") == null ? 100 : Integer.parseInt(req.queryParams("limit"));
+ int offset = req.queryParams("offset") == null ? 0 : Integer.parseInt(req.queryParams("offset"));
- return APIv3.getDatastore().createQuery(AuditLogEntry.class).order("performedAt").limit(limit).offset(offset).asList();
+ return APIv3.getDatastore().createQuery(AuditLogEntry.class).order("performedAt").limit(limit).offset(offset).asList();
+ } catch (NumberFormatException ex) {
+ return ErrorUtils.invalidInput(";imit and offset must be numerical inputs.");
+ }
}
}
\ No newline at end of file
diff --git a/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java b/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java
index 3bc3142..66ac4e6 100644
--- a/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java
+++ b/src/main/java/net/frozenorb/apiv3/routes/grants/DELETEGrant.java
@@ -31,7 +31,11 @@ public final class DELETEGrant implements Route {
return ErrorUtils.unauthorized(requiredPermission);
}
- String reason = req.queryParams("removalReason");
+ String reason = req.queryParams("reason");
+
+ if (reason == null || reason.trim().isEmpty()) {
+ return ErrorUtils.requiredInput("reason");
+ }
grant.delete(removedBy, reason);
// TODO: Fix IP
diff --git a/src/main/java/net/frozenorb/apiv3/routes/grants/GETGrants.java b/src/main/java/net/frozenorb/apiv3/routes/grants/GETGrants.java
index 33a03c0..37c33ca 100644
--- a/src/main/java/net/frozenorb/apiv3/routes/grants/GETGrants.java
+++ b/src/main/java/net/frozenorb/apiv3/routes/grants/GETGrants.java
@@ -2,6 +2,7 @@ package net.frozenorb.apiv3.routes.grants;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Grant;
+import net.frozenorb.apiv3.utils.ErrorUtils;
import spark.Request;
import spark.Response;
import spark.Route;
@@ -9,10 +10,14 @@ import spark.Route;
public final class GETGrants implements Route {
public Object handle(Request req, Response res) {
- int limit = req.queryParams("limit") == null ? 100 : Integer.parseInt(req.queryParams("limit"));
- int offset = req.queryParams("offset") == null ? 0 : Integer.parseInt(req.queryParams("offset"));
+ try {
+ int limit = req.queryParams("limit") == null ? 100 : Integer.parseInt(req.queryParams("limit"));
+ int offset = req.queryParams("offset") == null ? 0 : Integer.parseInt(req.queryParams("offset"));
- return APIv3.getDatastore().createQuery(Grant.class).order("addedAt").limit(limit).offset(offset).asList();
+ return APIv3.getDatastore().createQuery(Grant.class).order("addedAt").limit(limit).offset(offset).asList();
+ } catch (NumberFormatException ex) {
+ return ErrorUtils.invalidInput("limit and offset must be numerical inputs.");
+ }
}
}
\ No newline at end of file
diff --git a/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java b/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java
index dee4623..25a4d49 100644
--- a/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java
+++ b/src/main/java/net/frozenorb/apiv3/routes/grants/POSTUserGrant.java
@@ -26,8 +26,8 @@ public final class POSTUserGrant implements Route {
String reason = req.queryParams("reason");
- if (reason.trim().isEmpty()) {
- return ErrorUtils.invalidInput("A reason must be provided.");
+ if (reason == null || reason.trim().isEmpty()) {
+ return ErrorUtils.requiredInput("reason");
}
Set scopes = new HashSet<>();
diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java
index 813b2d0..bccac9a 100644
--- a/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java
+++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/DELETEPunishment.java
@@ -33,6 +33,10 @@ public final class DELETEPunishment implements Route {
String reason = req.queryParams("removalReason");
+ if (reason == null || reason.trim().isEmpty()) {
+ return ErrorUtils.requiredInput("reason");
+ }
+
punishment.delete(removedBy, reason);
// TODO: Fix IP
AuditLog.log(removedBy, "", req.attribute("actor"), AuditLogActionType.DELETE_PUNISHMENT, new Document("punishmentId", punishment.getId()));
diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishments.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishments.java
index 54b6390..52b1946 100644
--- a/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishments.java
+++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/GETPunishments.java
@@ -2,6 +2,7 @@ package net.frozenorb.apiv3.routes.punishments;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Punishment;
+import net.frozenorb.apiv3.utils.ErrorUtils;
import spark.Request;
import spark.Response;
import spark.Route;
@@ -9,10 +10,14 @@ import spark.Route;
public final class GETPunishments implements Route {
public Object handle(Request req, Response res) {
- int limit = req.queryParams("limit") == null ? 100 : Integer.parseInt(req.queryParams("limit"));
- int offset = req.queryParams("offset") == null ? 0 : Integer.parseInt(req.queryParams("offset"));
+ try {
+ int limit = req.queryParams("limit") == null ? 100 : Integer.parseInt(req.queryParams("limit"));
+ int offset = req.queryParams("offset") == null ? 0 : Integer.parseInt(req.queryParams("offset"));
- return APIv3.getDatastore().createQuery(Punishment.class).order("addedAt").limit(limit).offset(offset).asList();
+ return APIv3.getDatastore().createQuery(Punishment.class).order("addedAt").limit(limit).offset(offset).asList();
+ } catch (NumberFormatException ex) {
+ return ErrorUtils.invalidInput("limit and offset must be numerical inputs.");
+ }
}
}
\ No newline at end of file
diff --git a/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java b/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java
index 07479e0..503f2bb 100644
--- a/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java
+++ b/src/main/java/net/frozenorb/apiv3/routes/punishments/POSTUserPunish.java
@@ -17,14 +17,16 @@ public final class POSTUserPunish implements Route {
public Object handle(Request req, Response res) {
User target = User.byId(req.params("id"));
+ // TODO: PROTECTED USERS
+
if (target == null) {
return ErrorUtils.notFound("User", req.params("id"));
}
String reason = req.queryParams("reason");
- if (reason.trim().isEmpty()) {
- return ErrorUtils.invalidInput("A reason must be provided.");
+ if (reason == null || reason.trim().isEmpty()) {
+ return ErrorUtils.requiredInput("reason");
}
Punishment.PunishmentType type = Punishment.PunishmentType.valueOf(req.queryParams("type"));
diff --git a/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServer.java b/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServer.java
index f182963..c92281c 100644
--- a/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServer.java
+++ b/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServer.java
@@ -4,6 +4,7 @@ import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Server;
import net.frozenorb.apiv3.models.ServerGroup;
import net.frozenorb.apiv3.utils.ErrorUtils;
+import net.frozenorb.apiv3.utils.IPUtils;
import spark.Request;
import spark.Response;
import spark.Route;
@@ -22,6 +23,10 @@ public final class POSTServer implements Route {
return ErrorUtils.notFound("Server group", req.queryParams("group"));
}
+ if (!IPUtils.isValidIP(ip)) {
+ return ErrorUtils.invalidInput("IP address \"" + ip + "\" is not valid.");
+ }
+
Server server = new Server(id, bungeeId, displayName, apiKey, group, ip);
APIv3.getDatastore().save(server);
return server;
diff --git a/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerMetrics.java b/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerMetrics.java
index 2ae3429..048aa21 100644
--- a/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerMetrics.java
+++ b/src/main/java/net/frozenorb/apiv3/routes/servers/POSTServerMetrics.java
@@ -1,20 +1,14 @@
package net.frozenorb.apiv3.routes.servers;
import com.google.common.collect.ImmutableMap;
-import com.librato.metrics.LibratoBatch;
-import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.actors.Actor;
import net.frozenorb.apiv3.actors.ActorType;
import net.frozenorb.apiv3.models.Server;
-import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils;
-import org.bson.Document;
import spark.Request;
import spark.Response;
import spark.Route;
-import java.util.*;
-
public final class POSTServerMetrics implements Route {
public Object handle(Request req, Response res) {
diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java
index 701ef24..c0e68f0 100644
--- a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java
+++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserDetails.java
@@ -20,7 +20,8 @@ public final class GETUserDetails implements Route {
"user", user,
"grants", user.getGrants(),
"ipLog", user.getIPLog(),
- "punishments", user.getPunishments()
+ "punishments", user.getPunishments(),
+ "totpRequired", user.getTotpSecret() != null
);
}
diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/GETUserRequiresTOTP.java b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserRequiresTOTP.java
new file mode 100644
index 0000000..7595acd
--- /dev/null
+++ b/src/main/java/net/frozenorb/apiv3/routes/users/GETUserRequiresTOTP.java
@@ -0,0 +1,4 @@
+package net.frozenorb.apiv3.routes.users;
+
+public class GETUserRequiresTOTP {
+}
diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLogin.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLogin.java
index f3c16b9..77fe0b2 100644
--- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLogin.java
+++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserLogin.java
@@ -6,6 +6,7 @@ import net.frozenorb.apiv3.actors.ActorType;
import net.frozenorb.apiv3.models.Server;
import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils;
+import net.frozenorb.apiv3.utils.IPUtils;
import spark.Request;
import spark.Response;
import spark.Route;
@@ -24,6 +25,10 @@ public final class POSTUserLogin implements Route {
return ErrorUtils.serverOnly();
}
+ if (!IPUtils.isValidIP(userIp)) {
+ return ErrorUtils.invalidInput("IP address \"" + userIp + "\" is not valid.");
+ }
+
if (user == null) {
user = new User(UUID.fromString(req.params("id")), username);
APIv3.getDatastore().save(user);
diff --git a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java
index e6f3c60..e8dd2b3 100644
--- a/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java
+++ b/src/main/java/net/frozenorb/apiv3/routes/users/POSTUserRegister.java
@@ -19,9 +19,12 @@ import java.util.regex.Pattern;
public final class POSTUserRegister implements Route {
- private static final Pattern VALID_EMAIL_ADDRESS_REGEX =
+ private static final Pattern VALID_EMAIL_PATTERN =
Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE);
+ // TODO: POSSIBLE? perms check
+ // TODO: make messages better
+
public Object handle(Request req, Response res) {
User user = User.byId(req.params("id"));
@@ -35,7 +38,7 @@ public final class POSTUserRegister implements Route {
String email = req.queryParams("email");
- if (!VALID_EMAIL_ADDRESS_REGEX.matcher(email).find()) {
+ if (!VALID_EMAIL_PATTERN.matcher(email).matches()) {
return ErrorUtils.error(email + " is not a valid email.");
}
diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/LoggingExceptionHandler.java b/src/main/java/net/frozenorb/apiv3/unsorted/LoggingExceptionHandler.java
index 4eb4d47..645fee5 100644
--- a/src/main/java/net/frozenorb/apiv3/unsorted/LoggingExceptionHandler.java
+++ b/src/main/java/net/frozenorb/apiv3/unsorted/LoggingExceptionHandler.java
@@ -15,7 +15,7 @@ public final class LoggingExceptionHandler implements ExceptionHandler {
String code = new ObjectId().toHexString();
log.error(code + ":", ex);
- res.body(APIv3.getGson().toJson(ErrorUtils.error("An unknown error has occurred. Please contact a developer with the code \"" + code + "\".")));
+ res.body(APIv3.getGson().toJson(ErrorUtils.error("An unknown error has occurred. Please contact an API developer with the code \"" + code + "\".")));
}
}
\ No newline at end of file
diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java b/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java
index 2f6980d..908c82f 100644
--- a/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java
+++ b/src/main/java/net/frozenorb/apiv3/unsorted/Notification.java
@@ -45,6 +45,7 @@ public final class Notification {
}
public void sendAsText(String phoneNumber) throws IOException {
+ // TODO
throw new IOException(new NotImplementedException());
}
diff --git a/src/main/java/net/frozenorb/apiv3/unsorted/Permissions.java b/src/main/java/net/frozenorb/apiv3/unsorted/Permissions.java
index 143b0b8..7d31a74 100644
--- a/src/main/java/net/frozenorb/apiv3/unsorted/Permissions.java
+++ b/src/main/java/net/frozenorb/apiv3/unsorted/Permissions.java
@@ -9,6 +9,6 @@ public class Permissions {
public static final String CREATE_GRANT = "minehq.grant.create"; // minehq.grant.create.%RANK%
public static final String REMOVE_PUNISHMENT = "minehq.punishment.remove"; // minehq.punishment.remove.%TYPE%
- public static final String CREATE_PUNISHMENT = "minehq.punishment.create"; // minehq.punishment.remove.%TYPE%
+ public static final String CREATE_PUNISHMENT = "minehq.punishment.create"; // minehq.punishment.create.%TYPE%
}
\ No newline at end of file
diff --git a/src/main/java/net/frozenorb/apiv3/utils/ErrorUtils.java b/src/main/java/net/frozenorb/apiv3/utils/ErrorUtils.java
index a8ae71a..74a839c 100644
--- a/src/main/java/net/frozenorb/apiv3/utils/ErrorUtils.java
+++ b/src/main/java/net/frozenorb/apiv3/utils/ErrorUtils.java
@@ -24,6 +24,10 @@ public class ErrorUtils {
return error("Invalid input: " + message);
}
+ public static Map requiredInput(String field) {
+ return error("Field \"" + field + "\" is required.");
+ }
+
public static Map error(String message) {
return ImmutableMap.of(
"success", false,
diff --git a/src/main/java/net/frozenorb/apiv3/utils/IPUtils.java b/src/main/java/net/frozenorb/apiv3/utils/IPUtils.java
new file mode 100644
index 0000000..7f868e9
--- /dev/null
+++ b/src/main/java/net/frozenorb/apiv3/utils/IPUtils.java
@@ -0,0 +1,20 @@
+package net.frozenorb.apiv3.utils;
+
+import lombok.experimental.UtilityClass;
+
+import java.util.regex.Pattern;
+
+@UtilityClass
+public class IPUtils {
+
+ private static final Pattern VALID_IP_PATTERN = Pattern.compile(
+ "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
+ "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
+ "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
+ "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");
+
+ public static boolean isValidIP(String ip) {
+ return ip != null && VALID_IP_PATTERN.matcher(ip).matches();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/frozenorb/apiv3/utils/TOTPUtils.java b/src/main/java/net/frozenorb/apiv3/utils/TOTPUtils.java
index 0438650..04c954f 100644
--- a/src/main/java/net/frozenorb/apiv3/utils/TOTPUtils.java
+++ b/src/main/java/net/frozenorb/apiv3/utils/TOTPUtils.java
@@ -1,12 +1,14 @@
package net.frozenorb.apiv3.utils;
-import com.google.common.collect.ImmutableList;
import com.warrenstrange.googleauth.GoogleAuthenticator;
import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator;
import lombok.experimental.UtilityClass;
+import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.User;
-import org.apache.http.client.utils.URIBuilder;
+import redis.clients.jedis.Jedis;
+
+import java.util.concurrent.TimeUnit;
@UtilityClass
public class TOTPUtils {
@@ -23,18 +25,40 @@ public class TOTPUtils {
public static String getQRCodeURL(User user, GoogleAuthenticatorKey key) {
return GoogleAuthenticatorQRGenerator.getOtpAuthURL(
- "MineHQ v3",
+ "MineHQ Network",
user.getLastUsername(),
key
);
}
- public static boolean wasRecentlyUsed(User user, int code) {
+ public static boolean isPreAuthorized(User user, String ip) {
+ try (Jedis redis = APIv3.getRedisPool().getResource()) {
+ return redis.exists(user.getId() + ":preAuthorizedIP:" + ip.toLowerCase());
+ }
+ }
+ public static void markPreAuthorized(User user, String ip, long duration, TimeUnit unit) {
+ try (Jedis redis = APIv3.getRedisPool().getResource()) {
+ String key = user.getId() + ":preAuthorizedIP:" + ip.toLowerCase();
+
+ redis.set(key, "");
+ redis.expire(key, (int) unit.toSeconds(duration));
+ }
+ }
+
+ public static boolean wasRecentlyUsed(User user, int code) {
+ try (Jedis redis = APIv3.getRedisPool().getResource()) {
+ return redis.exists(user.getId() + ":recentTOTPCodes:" + code);
+ }
}
public static void markRecentlyUsed(User user, int code) {
+ try (Jedis redis = APIv3.getRedisPool().getResource()) {
+ String key = user.getId() + ":recentTOTPCodes:" + code;
+ redis.set(key, "");
+ redis.expire(key, (int) TimeUnit.MINUTES.toSeconds(5));
+ }
}
}
\ No newline at end of file