From 394c022fd48dda997beb71d2841b2e85789b68bb Mon Sep 17 00:00:00 2001 From: Daniel Gultsch <daniel@gultsch.de> Date: Sat, 23 Oct 2021 17:39:06 +0200 Subject: [PATCH] apply google java format --- .../de/gultsch/xmpp/addr/adapter/Adapter.java | 5 +- .../addr/adapter/gson/JidDeserializer.java | 11 +- .../xmpp/addr/adapter/gson/JidSerializer.java | 6 +- .../xmpp/addr/adapter/sql2o/JidConverter.java | 5 +- src/main/java/im/quicksy/server/Main.java | 157 +-- src/main/java/im/quicksy/server/Utils.java | 6 +- .../server/configuration/Configuration.java | 33 +- .../configuration/DatabaseConfiguration.java | 6 +- .../DatabaseConfigurationBundle.java | 6 +- .../controller/AuthenticationController.java | 194 ++-- .../server/controller/BaseController.java | 76 +- .../server/controller/EnterController.java | 908 ++++++++++-------- .../server/controller/PasswordController.java | 214 +++-- .../controller/SynchronizationController.java | 99 +- .../im/quicksy/server/database/Database.java | 76 +- .../database/LocalDateTimeConverter.java | 12 +- .../server/database/PhoneNumberConverter.java | 8 +- .../im/quicksy/server/database/SqlQuery.java | 31 +- .../server/database/UUIDStringConverter.java | 8 +- .../server/ejabberd/MyEjabberdApi.java | 1 - .../server/json/DurationDeserializer.java | 7 +- .../server/json/PatternDeserializer.java | 1 - .../server/json/VersionDeserializer.java | 7 +- .../java/im/quicksy/server/pojo/Device.java | 1 - .../java/im/quicksy/server/pojo/Entry.java | 1 - .../java/im/quicksy/server/pojo/Payment.java | 4 +- .../im/quicksy/server/pojo/PaymentMethod.java | 4 +- .../im/quicksy/server/pojo/PaymentStatus.java | 3 +- .../quicksy/server/pojo/ShoppingCartItem.java | 1 - .../java/im/quicksy/server/pojo/Voucher.java | 15 +- .../quicksy/server/throttle/RateLimiter.java | 16 +- .../im/quicksy/server/throttle/Strategy.java | 3 +- .../server/throttle/VolumeAttempt.java | 2 - .../server/throttle/VolumeLimiter.java | 20 +- .../im/quicksy/server/utils/CimUtils.java | 21 +- .../quicksy/server/utils/CodeGenerator.java | 5 +- .../java/im/quicksy/server/utils/PayPal.java | 102 +- .../AbstractVerificationProvider.java | 5 +- .../FixedPinVerificationProvider.java | 5 +- .../MetaVerificationProvider.java | 51 +- .../MockVerificationProvider.java | 15 +- .../NexmoVerificationProvider.java | 93 +- .../verification/RequestFailedException.java | 2 +- .../verification/TokenExpiredException.java | 2 +- .../TwilioVerificationProvider.java | 52 +- .../verification/VerificationProvider.java | 4 +- .../verification/nexmo/ErrorResponse.java | 1 - .../verification/nexmo/GenericResponse.java | 2 +- .../verification/twilio/ErrorResponse.java | 1 - .../verification/twilio/StartResponse.java | 1 - .../server/xmpp/synchronization/Entry.java | 22 +- .../xmpp/synchronization/PhoneBook.java | 19 +- .../xmpp/synchronization/package-info.java | 2 +- .../java/im/quicksy/server/DatabaseTest.java | 33 +- .../server/NexmoVerificationProviderTest.java | 3 +- .../quicksy/server/VolumeRateLimiterTest.java | 76 +- 56 files changed, 1355 insertions(+), 1109 deletions(-) diff --git a/src/main/java/de/gultsch/xmpp/addr/adapter/Adapter.java b/src/main/java/de/gultsch/xmpp/addr/adapter/Adapter.java index ed6eb32..ecbe336 100644 --- a/src/main/java/de/gultsch/xmpp/addr/adapter/Adapter.java +++ b/src/main/java/de/gultsch/xmpp/addr/adapter/Adapter.java @@ -4,11 +4,10 @@ import com.google.gson.GsonBuilder; import de.gultsch.xmpp.addr.adapter.gson.JidDeserializer; import de.gultsch.xmpp.addr.adapter.gson.JidSerializer; import de.gultsch.xmpp.addr.adapter.sql2o.JidConverter; +import java.util.Map; import org.sql2o.converters.Converter; import rocks.xmpp.addr.Jid; -import java.util.Map; - public class Adapter { public static void register(GsonBuilder gsonBuilder) { @@ -16,7 +15,6 @@ public class Adapter { gsonBuilder.registerTypeAdapter(Jid.class, new JidSerializer()); } - public static void register(Map<Class, Converter> converters) { final JidConverter jidConverter = new JidConverter(); converters.put(Jid.class, jidConverter); @@ -26,5 +24,4 @@ public class Adapter { throw new AssertionError(e); } } - } diff --git a/src/main/java/de/gultsch/xmpp/addr/adapter/gson/JidDeserializer.java b/src/main/java/de/gultsch/xmpp/addr/adapter/gson/JidDeserializer.java index e49e22a..84be93d 100644 --- a/src/main/java/de/gultsch/xmpp/addr/adapter/gson/JidDeserializer.java +++ b/src/main/java/de/gultsch/xmpp/addr/adapter/gson/JidDeserializer.java @@ -4,14 +4,17 @@ import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; -import rocks.xmpp.addr.Jid; - import java.lang.reflect.Type; +import rocks.xmpp.addr.Jid; public class JidDeserializer implements JsonDeserializer<Jid> { - public Jid deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + public Jid deserialize( + JsonElement jsonElement, + Type type, + JsonDeserializationContext jsonDeserializationContext) + throws JsonParseException { String jid = jsonElement.getAsString(); return Jid.ofEscaped(jid); } -} \ No newline at end of file +} diff --git a/src/main/java/de/gultsch/xmpp/addr/adapter/gson/JidSerializer.java b/src/main/java/de/gultsch/xmpp/addr/adapter/gson/JidSerializer.java index 9827484..c316144 100644 --- a/src/main/java/de/gultsch/xmpp/addr/adapter/gson/JidSerializer.java +++ b/src/main/java/de/gultsch/xmpp/addr/adapter/gson/JidSerializer.java @@ -1,13 +1,13 @@ package de.gultsch.xmpp.addr.adapter.gson; import com.google.gson.*; -import rocks.xmpp.addr.Jid; - import java.lang.reflect.Type; +import rocks.xmpp.addr.Jid; public class JidSerializer implements JsonSerializer<Jid> { - public JsonElement serialize(Jid jid, Type type, JsonSerializationContext jsonSerializationContext) { + public JsonElement serialize( + Jid jid, Type type, JsonSerializationContext jsonSerializationContext) { return new JsonPrimitive(jid.toEscapedString()); } } diff --git a/src/main/java/de/gultsch/xmpp/addr/adapter/sql2o/JidConverter.java b/src/main/java/de/gultsch/xmpp/addr/adapter/sql2o/JidConverter.java index e10cc81..a939b7c 100644 --- a/src/main/java/de/gultsch/xmpp/addr/adapter/sql2o/JidConverter.java +++ b/src/main/java/de/gultsch/xmpp/addr/adapter/sql2o/JidConverter.java @@ -17,7 +17,8 @@ public class JidConverter implements Converter<Jid> { throw new ConverterException("Unable to convert " + o.toString(), e); } } else { - throw new ConverterException("can not convert object of type " + o.getClass().getName() + " to Jid"); + throw new ConverterException( + "can not convert object of type " + o.getClass().getName() + " to Jid"); } } @@ -25,4 +26,4 @@ public class JidConverter implements Converter<Jid> { public Object toDatabaseParam(Jid jid) { return jid.asBareJid().toString(); } -} \ No newline at end of file +} diff --git a/src/main/java/im/quicksy/server/Main.java b/src/main/java/im/quicksy/server/Main.java index 19417ab..7111b4c 100644 --- a/src/main/java/im/quicksy/server/Main.java +++ b/src/main/java/im/quicksy/server/Main.java @@ -16,10 +16,14 @@ package im.quicksy.server; +import static spark.Spark.*; + import im.quicksy.server.configuration.Configuration; import im.quicksy.server.controller.*; import im.quicksy.server.xmpp.synchronization.Entry; import im.quicksy.server.xmpp.synchronization.PhoneBook; +import java.io.FileNotFoundException; +import java.util.Properties; import org.apache.commons.cli.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,11 +40,6 @@ import spark.TemplateEngine; import spark.template.freemarker.FreeMarkerEngine; import sun.misc.Signal; -import java.io.FileNotFoundException; -import java.util.Properties; - -import static spark.Spark.*; - public class Main { private static final Logger LOGGER = LoggerFactory.getLogger(Main.class); @@ -92,22 +91,23 @@ public class Main { properties.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug"); } - Signal.handle(new Signal("HUP"), signal -> { - try { - if (Configuration.reload()) { - LOGGER.info("reloaded config"); - logConfigurationInfo(); - } else { - LOGGER.error("unable to reload config. config file has moved"); - } - } catch (RuntimeException e) { - LOGGER.error("Unable to load config file - " + e.getMessage()); - } - }); + Signal.handle( + new Signal("HUP"), + signal -> { + try { + if (Configuration.reload()) { + LOGGER.info("reloaded config"); + logConfigurationInfo(); + } else { + LOGGER.error("unable to reload config. config file has moved"); + } + } catch (RuntimeException e) { + LOGGER.error("Unable to load config file - " + e.getMessage()); + } + }); setupWebServer(); setupXmppComponent(commandLine.hasOption("x")); - } private static void printHelp() { @@ -116,10 +116,18 @@ public class Main { } private static void logConfigurationInfo() { - LOGGER.info("validating phone numbers: " + Boolean.toString(Configuration.getInstance().isValidatePhoneNumbers())); - LOGGER.info("prevent registration when logged in with another device: " + Boolean.toString(Configuration.getInstance().isPreventRegistration())); - LOGGER.info("minimum client version: " + Configuration.getInstance().getMinVersion().toString()); - LOGGER.info("treat accounts as inactive after: " + Configuration.getInstance().getAccountInactivity()); + LOGGER.info( + "validating phone numbers: " + + Boolean.toString(Configuration.getInstance().isValidatePhoneNumbers())); + LOGGER.info( + "prevent registration when logged in with another device: " + + Boolean.toString(Configuration.getInstance().isPreventRegistration())); + LOGGER.info( + "minimum client version: " + + Configuration.getInstance().getMinVersion().toString()); + LOGGER.info( + "treat accounts as inactive after: " + + Configuration.getInstance().getAccountInactivity()); } private static void setupWebServer() { @@ -128,41 +136,55 @@ public class Main { final TemplateEngine templateEngine = new FreeMarkerEngine(); - path("/api", () -> { - get("/", BaseController.index); - path("/password", () -> { - before("", BaseController.versionCheck); - before("", PasswordController.throttleIp); - post("", PasswordController.setPassword); - }); - path("/authentication", () -> { - before("/*", BaseController.versionCheck); - before("/*", AuthenticationController.throttleIp); - get("/:phoneNumber", AuthenticationController.getAuthentication); - }); - }); - path("enter", () -> { - get("/", EnterController.intro, templateEngine); - get("/send-jabber-verification/", EnterController.getSendJabberVerification, templateEngine); - post("/send-jabber-verification/", EnterController.postSendJabberVerification); - get("/verify-jabber/", EnterController.getVerifyJabber, templateEngine); - post("/verify-jabber/", EnterController.postVerifyJabber); - get("/make-payment/", EnterController.getMakePayment, templateEngine); - get("/voucher/", EnterController.getVoucher, templateEngine); - post("/voucher/", EnterController.postVoucher); - get("/send-number-verification/", EnterController.getSendSmsVerification, templateEngine); - post("/send-number-verification/", EnterController.postSendSmsVerification); - get("/verify-number/", EnterController.getVerifyNumber, templateEngine); - post("/verify-number/", EnterController.postVerifyNumber); - get("/finished/", EnterController.getFinished, templateEngine); - get("/reset/", EnterController.getReset); - get("/confirm-reset/", EnterController.getConfirmReset, templateEngine); - get("/confirm-delete/", EnterController.getConfirmDelete, templateEngine); - get("/delete/", EnterController.getDelete, templateEngine); - post("/checkout/", EnterController.postCheckout); - get("/paypal/:status/:uuid/", EnterController.getPayPalResult); - get("/payment-received/", EnterController.getPaymentReceived, templateEngine); - }); + path( + "/api", + () -> { + get("/", BaseController.index); + path( + "/password", + () -> { + before("", BaseController.versionCheck); + before("", PasswordController.throttleIp); + post("", PasswordController.setPassword); + }); + path( + "/authentication", + () -> { + before("/*", BaseController.versionCheck); + before("/*", AuthenticationController.throttleIp); + get("/:phoneNumber", AuthenticationController.getAuthentication); + }); + }); + path( + "enter", + () -> { + get("/", EnterController.intro, templateEngine); + get( + "/send-jabber-verification/", + EnterController.getSendJabberVerification, + templateEngine); + post("/send-jabber-verification/", EnterController.postSendJabberVerification); + get("/verify-jabber/", EnterController.getVerifyJabber, templateEngine); + post("/verify-jabber/", EnterController.postVerifyJabber); + get("/make-payment/", EnterController.getMakePayment, templateEngine); + get("/voucher/", EnterController.getVoucher, templateEngine); + post("/voucher/", EnterController.postVoucher); + get( + "/send-number-verification/", + EnterController.getSendSmsVerification, + templateEngine); + post("/send-number-verification/", EnterController.postSendSmsVerification); + get("/verify-number/", EnterController.getVerifyNumber, templateEngine); + post("/verify-number/", EnterController.postVerifyNumber); + get("/finished/", EnterController.getFinished, templateEngine); + get("/reset/", EnterController.getReset); + get("/confirm-reset/", EnterController.getConfirmReset, templateEngine); + get("/confirm-delete/", EnterController.getConfirmDelete, templateEngine); + get("/delete/", EnterController.getDelete, templateEngine); + post("/checkout/", EnterController.postCheckout); + get("/paypal/:status/:uuid/", EnterController.getPayPalResult); + get("/payment-received/", EnterController.getPaymentReceived, templateEngine); + }); } private static void setupXmppComponent(final boolean debug) { @@ -173,21 +195,23 @@ public class Main { builder.extensions(Extension.of(Entry.class, PhoneBook.class)); - final ExternalComponent externalComponent = ExternalComponent.create( - Configuration.getInstance().getXmpp().getJid().toEscapedString(), - Configuration.getInstance().getXmpp().getSecret(), - builder.build(), - Configuration.getInstance().getXmpp().getHost(), - Configuration.getInstance().getXmpp().getPort() - ); + final ExternalComponent externalComponent = + ExternalComponent.create( + Configuration.getInstance().getXmpp().getJid().toEscapedString(), + Configuration.getInstance().getXmpp().getSecret(), + builder.build(), + Configuration.getInstance().getXmpp().getHost(), + Configuration.getInstance().getXmpp().getPort()); - ServiceDiscoveryManager serviceDiscoveryManager = externalComponent.getManager(ServiceDiscoveryManager.class); + ServiceDiscoveryManager serviceDiscoveryManager = + externalComponent.getManager(ServiceDiscoveryManager.class); serviceDiscoveryManager.addFeature(PhoneBook.NAMESPACE); serviceDiscoveryManager.addIdentity(Identity.storeGeneric()); externalComponent.disableFeature(Muc.NAMESPACE); externalComponent.disableFeature(Socks5ByteStream.NAMESPACE); - externalComponent.addIQHandler(PhoneBook.class, SynchronizationController.synchronize, true); + externalComponent.addIQHandler( + PhoneBook.class, SynchronizationController.synchronize, true); connectAndKeepRetrying(externalComponent); } @@ -204,5 +228,4 @@ public class Main { Utils.sleep(RETRY_INTERVAL); } } - } diff --git a/src/main/java/im/quicksy/server/Utils.java b/src/main/java/im/quicksy/server/Utils.java index 173bb7c..5fc7f99 100644 --- a/src/main/java/im/quicksy/server/Utils.java +++ b/src/main/java/im/quicksy/server/Utils.java @@ -31,6 +31,10 @@ public class Utils { } public static Jid jidOf(Phonenumber.PhoneNumber phoneNumber) { - return Jid.of(PhoneNumberUtil.getInstance().format(phoneNumber,PhoneNumberUtil.PhoneNumberFormat.E164), Configuration.getInstance().getDomain(),null); + return Jid.of( + PhoneNumberUtil.getInstance() + .format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164), + Configuration.getInstance().getDomain(), + null); } } diff --git a/src/main/java/im/quicksy/server/configuration/Configuration.java b/src/main/java/im/quicksy/server/configuration/Configuration.java index b69a22d..3a75696 100644 --- a/src/main/java/im/quicksy/server/configuration/Configuration.java +++ b/src/main/java/im/quicksy/server/configuration/Configuration.java @@ -16,7 +16,6 @@ package im.quicksy.server.configuration; - import com.github.zafarkhaja.semver.Version; import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; @@ -26,14 +25,13 @@ import de.gultsch.xmpp.addr.adapter.Adapter; import im.quicksy.server.json.DurationDeserializer; import im.quicksy.server.json.PatternDeserializer; import im.quicksy.server.json.VersionDeserializer; -import rocks.xmpp.addr.Jid; - import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.time.Duration; import java.util.*; import java.util.regex.Pattern; +import rocks.xmpp.addr.Jid; public class Configuration { @@ -54,13 +52,12 @@ public class Configuration { private boolean validatePhoneNumbers = true; private boolean preventRegistration = true; - private Configuration() { + private Configuration() {} - } - - public synchronized static void setFilename(String filename) throws FileNotFoundException { + public static synchronized void setFilename(String filename) throws FileNotFoundException { if (INSTANCE != null) { - throw new IllegalStateException("Unable to set filename after instance has been created"); + throw new IllegalStateException( + "Unable to set filename after instance has been created"); } Configuration.FILE = new File(filename); if (!Configuration.FILE.exists()) { @@ -68,7 +65,7 @@ public class Configuration { } } - public synchronized static Configuration getInstance() { + public static synchronized Configuration getInstance() { if (INSTANCE == null) { INSTANCE = load(); } @@ -93,7 +90,7 @@ public class Configuration { } } - public synchronized static boolean reload() { + public static synchronized boolean reload() { if (Configuration.FILE.exists()) { final Configuration newConfig = load(); if (!newConfig.check()) { @@ -107,7 +104,15 @@ public class Configuration { } public boolean check() { - return domain != null && minVersion != null && web != null && xmpp != null && xmpp.check() && db != null && db.size() == 2 && payPal != null && payPal.check(); + return domain != null + && minVersion != null + && web != null + && xmpp != null + && xmpp.check() + && db != null + && db.size() == 2 + && payPal != null + && payPal.check(); } public Duration getAccountInactivity() { @@ -131,7 +136,10 @@ public class Configuration { } public DatabaseConfigurationBundle getDatabaseConfigurationBundle() { - return new DatabaseConfigurationBundle.Builder().setEjabberdConfiguration(db.get("ejabberd")).setQuicksyConfiguration(db.get("quicksy")).build(); + return new DatabaseConfigurationBundle.Builder() + .setEjabberdConfiguration(db.get("ejabberd")) + .setQuicksyConfiguration(db.get("quicksy")) + .build(); } public Optional<String> getCimAuthToken() { @@ -220,7 +228,6 @@ public class Configuration { } } - public static class ProviderConfiguration { private Map<String, String> parameter; private List<Integer> deny; diff --git a/src/main/java/im/quicksy/server/configuration/DatabaseConfiguration.java b/src/main/java/im/quicksy/server/configuration/DatabaseConfiguration.java index 1912e4c..f99f4a0 100644 --- a/src/main/java/im/quicksy/server/configuration/DatabaseConfiguration.java +++ b/src/main/java/im/quicksy/server/configuration/DatabaseConfiguration.java @@ -23,9 +23,7 @@ public class DatabaseConfiguration { private String password; private int poolSize = 1; - private DatabaseConfiguration() { - - } + private DatabaseConfiguration() {} public String getUsername() { return username; @@ -66,4 +64,4 @@ public class DatabaseConfiguration { return configuration; } } -} \ No newline at end of file +} diff --git a/src/main/java/im/quicksy/server/configuration/DatabaseConfigurationBundle.java b/src/main/java/im/quicksy/server/configuration/DatabaseConfigurationBundle.java index cded522..9997d08 100644 --- a/src/main/java/im/quicksy/server/configuration/DatabaseConfigurationBundle.java +++ b/src/main/java/im/quicksy/server/configuration/DatabaseConfigurationBundle.java @@ -21,9 +21,7 @@ public class DatabaseConfigurationBundle { private DatabaseConfiguration ejabberd; private DatabaseConfiguration quicksy; - private DatabaseConfigurationBundle() { - - } + private DatabaseConfigurationBundle() {} public DatabaseConfiguration getEjabberdConfiguration() { return ejabberd; @@ -33,7 +31,6 @@ public class DatabaseConfigurationBundle { return quicksy; } - public static class Builder { private final DatabaseConfigurationBundle bundle = new DatabaseConfigurationBundle(); @@ -51,6 +48,5 @@ public class DatabaseConfigurationBundle { public DatabaseConfigurationBundle build() { return bundle; } - } } diff --git a/src/main/java/im/quicksy/server/controller/AuthenticationController.java b/src/main/java/im/quicksy/server/controller/AuthenticationController.java index 86e6390..30d0cd0 100644 --- a/src/main/java/im/quicksy/server/controller/AuthenticationController.java +++ b/src/main/java/im/quicksy/server/controller/AuthenticationController.java @@ -16,11 +16,13 @@ package im.quicksy.server.controller; +import static spark.Spark.halt; + import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.Phonenumber; -import im.quicksy.server.configuration.Configuration; import im.quicksy.server.Utils; +import im.quicksy.server.configuration.Configuration; import im.quicksy.server.ejabberd.MyEjabberdApi; import im.quicksy.server.pojo.Device; import im.quicksy.server.throttle.RateLimiter; @@ -28,19 +30,16 @@ import im.quicksy.server.throttle.Strategy; import im.quicksy.server.verification.RequestFailedException; import im.quicksy.server.verification.TwilioVerificationProvider; import im.quicksy.server.verification.VerificationProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import rocks.xmpp.addr.Jid; -import spark.Filter; -import spark.Route; - import java.net.InetAddress; import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.Locale; - -import static spark.Spark.halt; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import rocks.xmpp.addr.Jid; +import spark.Filter; +import spark.Route; public class AuthenticationController extends BaseController { @@ -48,85 +47,100 @@ public class AuthenticationController extends BaseController { private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationController.class); - private static final RateLimiter<InetAddress> PER_IP_RATE_LIMITER = RateLimiter.of( - Strategy.of(Duration.ofMinutes(5), 5), - Strategy.of(Duration.ofDays(1), 100) - ); - - private static final RateLimiter<Phonenumber.PhoneNumber> PER_PHONE_NUMBER_LIMITER = RateLimiter.of( - Strategy.of(Duration.ofMinutes(2), 1), - Strategy.of(Duration.ofHours(8), 2) - ); - - private static final RateLimiter<Device> PER_DEVICE_LIMITER = RateLimiter.of( - Strategy.of(Duration.ofDays(1), 3) - ); - - public static Route getAuthentication = (request, response) -> { - final String userSuppliedPhoneNumber = request.params("phoneNumber"); - final String installationId = request.headers("Installation-Id"); - final String userSuppliedLanguage = request.headers("Accept-Language"); - if (!E164_PATTERN.matcher(userSuppliedPhoneNumber).matches()) { - return halt(400, "phone number is not formatted to E164"); - } - if (installationId == null || !UUID_PATTERN.matcher(installationId).matches() || userSuppliedLanguage == null) { - return halt(400, "Missing installation id"); - } - - final Phonenumber.PhoneNumber phoneNumber; - try { - phoneNumber = PhoneNumberUtil.getInstance().parse(userSuppliedPhoneNumber, "DE"); - } catch (NumberParseException e) { - return halt(400, "Unable to parse phone number"); - } - - if (Configuration.getInstance().isValidatePhoneNumbers() && !PhoneNumberUtil.getInstance().isValidNumber(phoneNumber)) { - LOGGER.info("libphonenumber reported " + phoneNumber + " as invalid"); - return halt(400); - } - - final Jid jid = Utils.jidOf(phoneNumber); - if (Configuration.getInstance().isPreventRegistration() && MyEjabberdApi.getInstance().getUserResources(jid.getEscapedLocal(), jid.getDomain()).size() > 0) { - return halt(409); - } - - try { - PER_PHONE_NUMBER_LIMITER.attempt(phoneNumber); - PER_DEVICE_LIMITER.attempt(new Device(installationId)); - } catch (RateLimiter.RetryInException e) { - response.header("Retry-After", String.valueOf(e.getInterval().getSeconds())); - LOGGER.info(e.getMessage()); - return halt(429, e.getMessage()); - } - - final String language; - if (AVAILABLE_LANGUAGES.contains(userSuppliedLanguage.toLowerCase())) { - language = userSuppliedLanguage.toLowerCase(); - } else { - language = null; - } - - try { - VERIFICATION_PROVIDER.request(phoneNumber, VerificationProvider.Method.SMS, language); - } catch (RequestFailedException e) { - if (e.getCode() == TwilioVerificationProvider.PHONE_NUMBER_IS_INVALID) { - LOGGER.info("verification provider said " + phoneNumber + " is invalid"); - return halt(400); - } - LOGGER.warn("unable to send SMS verification message to " + phoneNumber + " (" + e.getMessage() + ", code=" + e.getCode() + ")"); - halt(500); - } - - return ""; - }; - - public static Filter throttleIp = (request, response) -> { - try { - PER_IP_RATE_LIMITER.attempt(getClientIp(request)); - } catch (RateLimiter.RetryInException e) { - LOGGER.info(e.getMessage()); - response.header("Retry-After", String.valueOf(e.getInterval().getSeconds())); - halt(429, e.getMessage()); - } - }; + private static final RateLimiter<InetAddress> PER_IP_RATE_LIMITER = + RateLimiter.of( + Strategy.of(Duration.ofMinutes(5), 5), Strategy.of(Duration.ofDays(1), 100)); + + private static final RateLimiter<Phonenumber.PhoneNumber> PER_PHONE_NUMBER_LIMITER = + RateLimiter.of( + Strategy.of(Duration.ofMinutes(2), 1), Strategy.of(Duration.ofHours(8), 2)); + + private static final RateLimiter<Device> PER_DEVICE_LIMITER = + RateLimiter.of(Strategy.of(Duration.ofDays(1), 3)); + + public static Route getAuthentication = + (request, response) -> { + final String userSuppliedPhoneNumber = request.params("phoneNumber"); + final String installationId = request.headers("Installation-Id"); + final String userSuppliedLanguage = request.headers("Accept-Language"); + if (!E164_PATTERN.matcher(userSuppliedPhoneNumber).matches()) { + return halt(400, "phone number is not formatted to E164"); + } + if (installationId == null + || !UUID_PATTERN.matcher(installationId).matches() + || userSuppliedLanguage == null) { + return halt(400, "Missing installation id"); + } + + final Phonenumber.PhoneNumber phoneNumber; + try { + phoneNumber = + PhoneNumberUtil.getInstance().parse(userSuppliedPhoneNumber, "DE"); + } catch (NumberParseException e) { + return halt(400, "Unable to parse phone number"); + } + + if (Configuration.getInstance().isValidatePhoneNumbers() + && !PhoneNumberUtil.getInstance().isValidNumber(phoneNumber)) { + LOGGER.info("libphonenumber reported " + phoneNumber + " as invalid"); + return halt(400); + } + + final Jid jid = Utils.jidOf(phoneNumber); + if (Configuration.getInstance().isPreventRegistration() + && MyEjabberdApi.getInstance() + .getUserResources(jid.getEscapedLocal(), jid.getDomain()) + .size() + > 0) { + return halt(409); + } + + try { + PER_PHONE_NUMBER_LIMITER.attempt(phoneNumber); + PER_DEVICE_LIMITER.attempt(new Device(installationId)); + } catch (RateLimiter.RetryInException e) { + response.header("Retry-After", String.valueOf(e.getInterval().getSeconds())); + LOGGER.info(e.getMessage()); + return halt(429, e.getMessage()); + } + + final String language; + if (AVAILABLE_LANGUAGES.contains(userSuppliedLanguage.toLowerCase())) { + language = userSuppliedLanguage.toLowerCase(); + } else { + language = null; + } + + try { + VERIFICATION_PROVIDER.request( + phoneNumber, VerificationProvider.Method.SMS, language); + } catch (RequestFailedException e) { + if (e.getCode() == TwilioVerificationProvider.PHONE_NUMBER_IS_INVALID) { + LOGGER.info("verification provider said " + phoneNumber + " is invalid"); + return halt(400); + } + LOGGER.warn( + "unable to send SMS verification message to " + + phoneNumber + + " (" + + e.getMessage() + + ", code=" + + e.getCode() + + ")"); + halt(500); + } + + return ""; + }; + + public static Filter throttleIp = + (request, response) -> { + try { + PER_IP_RATE_LIMITER.attempt(getClientIp(request)); + } catch (RateLimiter.RetryInException e) { + LOGGER.info(e.getMessage()); + response.header("Retry-After", String.valueOf(e.getInterval().getSeconds())); + halt(429, e.getMessage()); + } + }; } diff --git a/src/main/java/im/quicksy/server/controller/BaseController.java b/src/main/java/im/quicksy/server/controller/BaseController.java index 1e2719b..6abbe39 100644 --- a/src/main/java/im/quicksy/server/controller/BaseController.java +++ b/src/main/java/im/quicksy/server/controller/BaseController.java @@ -16,44 +16,44 @@ package im.quicksy.server.controller; +import static spark.Spark.halt; + import com.github.zafarkhaja.semver.ParseException; import com.github.zafarkhaja.semver.Version; import com.google.common.base.Splitter; import com.google.common.net.InetAddresses; import im.quicksy.server.configuration.Configuration; import im.quicksy.server.verification.MetaVerificationProvider; -import im.quicksy.server.verification.NexmoVerificationProvider; -import im.quicksy.server.verification.TwilioVerificationProvider; import im.quicksy.server.verification.VerificationProvider; +import java.net.InetAddress; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import spark.Filter; import spark.Request; import spark.Route; -import java.net.InetAddress; -import java.util.Collections; -import java.util.List; -import java.util.regex.Pattern; - -import static spark.Spark.halt; - public class BaseController { private static final Logger LOGGER = LoggerFactory.getLogger(BaseController.class); - public static Route index = (request, response) -> { - response.type("text/plain"); - return "This is Quicksy Server"; - }; + public static Route index = + (request, response) -> { + response.type("text/plain"); + return "This is Quicksy Server"; + }; protected static final String HEADER_X_REAL_IP = "X-Real-IP"; protected static final String HEADER_AUTHORIZATION = "Authorization"; public static Pattern E164_PATTERN = Pattern.compile("^\\+?[1-9]\\d{1,14}$"); protected static Pattern PIN_PATTERN = Pattern.compile("^[0-9]{6}$"); - protected static Pattern UUID_PATTERN = Pattern.compile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"); + protected static Pattern UUID_PATTERN = + Pattern.compile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"); - protected static final VerificationProvider VERIFICATION_PROVIDER = new MetaVerificationProvider(); + protected static final VerificationProvider VERIFICATION_PROVIDER = + new MetaVerificationProvider(); protected static InetAddress getClientIp(Request request) { final InetAddress remote = InetAddresses.forString(request.ip()); @@ -66,25 +66,33 @@ public class BaseController { return remote; } - public static Filter versionCheck = (request, response) -> { - final String userAgent = request.headers("User-Agent"); - LOGGER.info("Running version check against "+userAgent); - final List<String> parts = userAgent == null ? Collections.emptyList() : Splitter.on('/').limit(2).splitToList(userAgent); - if (parts.size() == 2) { - try { - Version version = Version.valueOf(parts.get(1)); - if (!"Quicksy".equals(parts.get(0)) || version.lessThan(Configuration.getInstance().getMinVersion())) { - LOGGER.warn("Outdated client version detected ("+userAgent+")"); + public static Filter versionCheck = + (request, response) -> { + final String userAgent = request.headers("User-Agent"); + LOGGER.info("Running version check against " + userAgent); + final List<String> parts = + userAgent == null + ? Collections.emptyList() + : Splitter.on('/').limit(2).splitToList(userAgent); + if (parts.size() == 2) { + try { + Version version = Version.valueOf(parts.get(1)); + if (!"Quicksy".equals(parts.get(0)) + || version.lessThan(Configuration.getInstance().getMinVersion())) { + LOGGER.warn("Outdated client version detected (" + userAgent + ")"); + halt(403); + } + } catch (ParseException e) { + LOGGER.warn( + "Unable to parse client version from User-Agent (" + + userAgent + + ")"); + halt(403); + } + } else { + LOGGER.warn( + "Unable to parse client version from User-Agent (" + userAgent + ")"); halt(403); } - } catch (ParseException e) { - LOGGER.warn("Unable to parse client version from User-Agent ("+userAgent+")"); - halt(403); - } - } else { - LOGGER.warn("Unable to parse client version from User-Agent ("+userAgent+")"); - halt(403); - } - }; - + }; } diff --git a/src/main/java/im/quicksy/server/controller/EnterController.java b/src/main/java/im/quicksy/server/controller/EnterController.java index 5a56f58..a41164a 100644 --- a/src/main/java/im/quicksy/server/controller/EnterController.java +++ b/src/main/java/im/quicksy/server/controller/EnterController.java @@ -31,490 +31,548 @@ import im.quicksy.server.pojo.Voucher; import im.quicksy.server.utils.CimUtils; import im.quicksy.server.utils.CodeGenerator; import im.quicksy.server.utils.PayPal; -import im.quicksy.server.verification.NexmoVerificationProvider; import im.quicksy.server.verification.TwilioVerificationProvider; import im.quicksy.server.verification.VerificationProvider; +import java.util.HashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import rocks.xmpp.addr.Jid; import spark.*; -import java.util.HashMap; - public class EnterController extends BaseController { private static final Logger LOGGER = LoggerFactory.getLogger(EnterController.class); - private static final VerificationProvider VERIFICATION_PROVIDER = new TwilioVerificationProvider(); + private static final VerificationProvider VERIFICATION_PROVIDER = + new TwilioVerificationProvider(); - public static TemplateViewRoute intro = (request, response) -> { - HashMap<String, Object> model = new HashMap<>(); - model.put("fee", Payment.FEE); - return new ModelAndView(model, "enter_index.ftl"); - }; - public static TemplateViewRoute getSendJabberVerification = (request, response) -> { - Session session = request.session(); - session.removeAttribute("jid"); - session.removeAttribute("code"); - session.removeAttribute("attempts"); - HashMap<String, Object> model = new HashMap<>(); - String error = request.queryParams("error"); - if (error != null) { - model.put("error", error); - } - return new ModelAndView(model, "enter_start.ftl"); - }; + public static TemplateViewRoute intro = + (request, response) -> { + HashMap<String, Object> model = new HashMap<>(); + model.put("fee", Payment.FEE); + return new ModelAndView(model, "enter_index.ftl"); + }; + public static TemplateViewRoute getSendJabberVerification = + (request, response) -> { + Session session = request.session(); + session.removeAttribute("jid"); + session.removeAttribute("code"); + session.removeAttribute("attempts"); + HashMap<String, Object> model = new HashMap<>(); + String error = request.queryParams("error"); + if (error != null) { + model.put("error", error); + } + return new ModelAndView(model, "enter_start.ftl"); + }; - public static Route postSendJabberVerification = (request, response) -> { - String input = request.queryParams("jid"); - if (input != null && !input.isEmpty()) { - try { - Jid jid = Jid.of(input); - if (jid.isDomainJid() || jid.isFullJid()) { - response.redirect("/enter/send-jabber-verification/?error=invalid"); - return ""; - } - if (jid.getDomain().equals(Configuration.getInstance().getDomain())) { - response.redirect("/enter/send-jabber-verification/?error=no-quicksy"); - return ""; - } - String from = Configuration.getInstance().getDomain(); - String code = CodeGenerator.generate(6); - String message = String.format("Your Quicksy verification code is %s", code); - MyEjabberdApi.getInstance().sendChatMessage(from, jid.asBareJid().toEscapedString(), "Your Quicksy verification code", message); - final Session session = request.session(true); - session.attribute("jid", jid.asBareJid()); - session.attribute("code", code); - session.attribute("attempts", 0); - response.redirect("/enter/verify-jabber/"); - return ""; - } catch (RequestFailedException e) { - LOGGER.error("unable to send Jabber verification message - " + e.getMessage()); - response.redirect("/enter/send-jabber-verification/?error=failed"); + public static Route postSendJabberVerification = + (request, response) -> { + String input = request.queryParams("jid"); + if (input != null && !input.isEmpty()) { + try { + Jid jid = Jid.of(input); + if (jid.isDomainJid() || jid.isFullJid()) { + response.redirect("/enter/send-jabber-verification/?error=invalid"); + return ""; + } + if (jid.getDomain().equals(Configuration.getInstance().getDomain())) { + response.redirect("/enter/send-jabber-verification/?error=no-quicksy"); + return ""; + } + String from = Configuration.getInstance().getDomain(); + String code = CodeGenerator.generate(6); + String message = + String.format("Your Quicksy verification code is %s", code); + MyEjabberdApi.getInstance() + .sendChatMessage( + from, + jid.asBareJid().toEscapedString(), + "Your Quicksy verification code", + message); + final Session session = request.session(true); + session.attribute("jid", jid.asBareJid()); + session.attribute("code", code); + session.attribute("attempts", 0); + response.redirect("/enter/verify-jabber/"); + return ""; + } catch (RequestFailedException e) { + LOGGER.error( + "unable to send Jabber verification message - " + e.getMessage()); + response.redirect("/enter/send-jabber-verification/?error=failed"); + return ""; + } catch (IllegalArgumentException e) { + // fallthrough + } + } + response.redirect("/enter/send-jabber-verification/?error=invalid"); return ""; - } catch (IllegalArgumentException e) { - //fallthrough - } - } - response.redirect("/enter/send-jabber-verification/?error=invalid"); - return ""; - }; + }; - public static TemplateViewRoute getVerifyJabber = (request, response) -> { - Session session = request.session(); - Jid jid = session.attribute("jid"); - String codeInSession = session.attribute("code"); - if (jid == null || codeInSession == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - HashMap<String, Object> model = new HashMap<>(); - model.put("jid", jid.toEscapedString()); - String error = request.queryParams("error"); - if (error != null) { - model.put("error", error); - } - return new ModelAndView(model, "enter_verify_jid.ftl"); - }; - public static Route postVerifyJabber = (request, response) -> { - Session session = request.session(); - Jid jid = session.attribute("jid"); - String codeInSession = session.attribute("code"); - Integer attempts = session.attribute("attempts"); - if (jid == null || codeInSession == null || attempts == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - String userEnteredCode = request.queryParams("code"); - if (codeInSession.equals(userEnteredCode)) { - session.attribute("jid-verified", true); - session.removeAttribute("code"); + public static TemplateViewRoute getVerifyJabber = + (request, response) -> { + Session session = request.session(); + Jid jid = session.attribute("jid"); + String codeInSession = session.attribute("code"); + if (jid == null || codeInSession == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + HashMap<String, Object> model = new HashMap<>(); + model.put("jid", jid.toEscapedString()); + String error = request.queryParams("error"); + if (error != null) { + model.put("error", error); + } + return new ModelAndView(model, "enter_verify_jid.ftl"); + }; + public static Route postVerifyJabber = + (request, response) -> { + Session session = request.session(); + Jid jid = session.attribute("jid"); + String codeInSession = session.attribute("code"); + Integer attempts = session.attribute("attempts"); + if (jid == null || codeInSession == null || attempts == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + String userEnteredCode = request.queryParams("code"); + if (codeInSession.equals(userEnteredCode)) { + session.attribute("jid-verified", true); + session.removeAttribute("code"); - final Entry entry = Database.getInstance().getEntry(jid); - if (entry == null) { - if (CimUtils.isPayingAccount(jid)) { - if (createEntryAfterSuccessfulVoucher("c.im",jid)) { + final Entry entry = Database.getInstance().getEntry(jid); + if (entry == null) { + if (CimUtils.isPayingAccount(jid)) { + if (createEntryAfterSuccessfulVoucher("c.im", jid)) { + response.redirect("/enter/send-number-verification/"); + } else { + response.redirect("/enter/make-payment/"); + } + } else { + response.redirect("/enter/make-payment/"); + } + } else if (entry.isVerified()) { + response.redirect("/enter/finished/"); + } else if (entry.getPhoneNumber() != null) { + response.redirect("/enter/verify-number/"); + } else { response.redirect("/enter/send-number-verification/"); + } + } else { + attempts++; + if (attempts >= 5) { + session.removeAttribute("code"); + response.redirect("/enter/send-jabber-verification/?error=attempts"); } else { - response.redirect("/enter/make-payment/"); + session.attribute("attempts", attempts); + response.redirect("/enter/verify-jabber/?error=invalid"); + } + } + return ""; + }; + public static TemplateViewRoute getMakePayment = + (request, response) -> { + final Session session = request.session(); + final Jid jid = getVerifiedJid(session); + if (jid == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + HashMap<String, Object> model = new HashMap<>(); + model.put("jid", jid.toEscapedString()); + model.put("fee", Payment.FEE); + String error = request.queryParams("error"); + if (error != null) { + model.put("error", error); + } + return new ModelAndView(model, "enter_payment.ftl"); + }; + public static TemplateViewRoute getVoucher = + (request, response) -> { + final Session session = request.session(); + final Jid jid = getVerifiedJid(session); + if (jid == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + return new ModelAndView(null, "enter_voucher.ftl"); + }; + public static Route postVoucher = + (request, response) -> { + final Session session = request.session(); + final Jid jid = getVerifiedJid(session); + if (jid == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + final String voucher = request.queryParams("voucher"); + if (Voucher.checkVoucher(voucher)) { + if (createEntryAfterSuccessfulVoucher(voucher, jid)) { + response.redirect("/enter/send-number-verification/"); + } else { + response.redirect("/enter/make-payment/?error=redeem-voucher"); } } else { - response.redirect("/enter/make-payment/"); + response.redirect("/enter/make-payment/?error=voucher"); } - } else if (entry.isVerified()) { - response.redirect("/enter/finished/"); - } else if (entry.getPhoneNumber() != null) { - response.redirect("/enter/verify-number/"); - } else { - response.redirect("/enter/send-number-verification/"); - } - } else { - attempts++; - if (attempts >= 5) { - session.removeAttribute("code"); - response.redirect("/enter/send-jabber-verification/?error=attempts"); - } else { - session.attribute("attempts", attempts); - response.redirect("/enter/verify-jabber/?error=invalid"); - } - } - return ""; - }; - public static TemplateViewRoute getMakePayment = (request, response) -> { - final Session session = request.session(); - final Jid jid = getVerifiedJid(session); - if (jid == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - HashMap<String, Object> model = new HashMap<>(); - model.put("jid", jid.toEscapedString()); - model.put("fee", Payment.FEE); - String error = request.queryParams("error"); - if (error != null) { - model.put("error", error); - } - return new ModelAndView(model, "enter_payment.ftl"); - }; - public static TemplateViewRoute getVoucher = (request, response) -> { - final Session session = request.session(); - final Jid jid = getVerifiedJid(session); - if (jid == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - return new ModelAndView(null, "enter_voucher.ftl"); - }; - public static Route postVoucher = (request, response) -> { - final Session session = request.session(); - final Jid jid = getVerifiedJid(session); - if (jid == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - final String voucher = request.queryParams("voucher"); - if (Voucher.checkVoucher(voucher)) { - if (createEntryAfterSuccessfulVoucher(voucher, jid)) { - response.redirect("/enter/send-number-verification/"); - } else { - response.redirect("/enter/make-payment/?error=redeem-voucher"); - } - } else { - response.redirect("/enter/make-payment/?error=voucher"); - } - return null; - }; - public static TemplateViewRoute getSendSmsVerification = (request, response) -> { - final Session session = request.session(); - final Jid jid = getVerifiedJid(session); - if (jid == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - final HashMap<String, Object> model = new HashMap<>(); - String error = request.queryParams("error"); - final VerificationProvider.Method method = verificationMethod(request); - if (error != null) { - model.put("error", error); - } - model.put("method",method); - return new ModelAndView(model, "enter_phone_number.ftl"); - }; + return null; + }; + public static TemplateViewRoute getSendSmsVerification = + (request, response) -> { + final Session session = request.session(); + final Jid jid = getVerifiedJid(session); + if (jid == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + final HashMap<String, Object> model = new HashMap<>(); + String error = request.queryParams("error"); + final VerificationProvider.Method method = verificationMethod(request); + if (error != null) { + model.put("error", error); + } + model.put("method", method); + return new ModelAndView(model, "enter_phone_number.ftl"); + }; private static VerificationProvider.Method verificationMethod(Request request) { try { - return VerificationProvider.Method.valueOf(request.queryParamOrDefault("method","SMS")); + return VerificationProvider.Method.valueOf( + request.queryParamOrDefault("method", "SMS")); } catch (IllegalArgumentException e) { LOGGER.error(e.getMessage()); return VerificationProvider.Method.SMS; } } - public static Route postSendSmsVerification = (request, response) -> { - final Session session = request.session(); - final Jid jid = getVerifiedJid(session); - if (jid == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - final String input = request.queryParams("number"); - final VerificationProvider.Method method = verificationMethod(request); - final String number = input == null ? "" : CharMatcher.whitespace().removeFrom(input); - if (E164_PATTERN.matcher(number).matches()) { - try { - Phonenumber.PhoneNumber phoneNumber = PhoneNumberUtil.getInstance().parse(number, "us"); - if (Configuration.getInstance().isValidatePhoneNumbers() && !PhoneNumberUtil.getInstance().isValidNumber(phoneNumber)) { - LOGGER.info("libphonenumber reported " + phoneNumber + " as invalid"); + public static Route postSendSmsVerification = + (request, response) -> { + final Session session = request.session(); + final Jid jid = getVerifiedJid(session); + if (jid == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + final String input = request.queryParams("number"); + final VerificationProvider.Method method = verificationMethod(request); + final String number = + input == null ? "" : CharMatcher.whitespace().removeFrom(input); + if (E164_PATTERN.matcher(number).matches()) { + try { + Phonenumber.PhoneNumber phoneNumber = + PhoneNumberUtil.getInstance().parse(number, "us"); + if (Configuration.getInstance().isValidatePhoneNumbers() + && !PhoneNumberUtil.getInstance().isValidNumber(phoneNumber)) { + LOGGER.info("libphonenumber reported " + phoneNumber + " as invalid"); + response.redirect("/enter/send-number-verification/?error=invalid"); + return null; + } + final Entry entry = Database.getInstance().getEntry(jid); + if (entry == null) { + response.redirect("/enter/make-payment/?error=not-yet"); + return null; + } + + if (entry.isVerified() && entry.getPhoneNumber() != null) { + response.redirect("/enter/send-number-verification/?error=already"); + return null; + } + + if (entry.getAttempts() <= 0) { + response.redirect("/enter/send-number-verification/?error=attempts"); + return null; + } + + try { + VERIFICATION_PROVIDER.request(phoneNumber, method); + } catch (im.quicksy.server.verification.RequestFailedException e) { + if (e.getCode() == TwilioVerificationProvider.PHONE_NUMBER_IS_INVALID) { + LOGGER.info( + "verification provider said " + + phoneNumber + + " is invalid"); + response.redirect("/enter/send-number-verification/?error=invalid"); + return null; + } + LOGGER.warn( + "unable to send SMS verification message to " + + phoneNumber + + " (" + + e.getMessage() + + ", code=" + + e.getCode() + + ")"); + response.redirect("/enter/send-number-verification/?error=failed"); + return null; + } + entry.setPhoneNumber(phoneNumber); + entry.setVerified(false); + entry.decreaseAttempts(); + Database.getInstance().updateEntry(entry); + response.redirect("/enter/verify-number/?method=" + method.toString()); + } catch (NumberParseException e) { + response.redirect("/enter/send-number-verification/?error=invalid"); + } + } else { response.redirect("/enter/send-number-verification/?error=invalid"); + } + return null; + }; + public static TemplateViewRoute getVerifyNumber = + (request, response) -> { + final Session session = request.session(); + final Jid jid = getVerifiedJid(session); + if (jid == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); return null; } + final HashMap<String, Object> model = new HashMap<>(); + final Entry entry = Database.getInstance().getEntry(jid); + if (entry == null) { - response.redirect("/enter/make-payment/?error=not-yet"); + response.status(500); + return null; + } else if (entry.getPhoneNumber() == null) { + response.redirect("/enter/send-number-verification/"); return null; } - if (entry.isVerified() && entry.getPhoneNumber() != null) { - response.redirect("/enter/send-number-verification/?error=already"); + final String error = request.queryParams("error"); + if (error != null) { + model.put("error", error); + } + model.put("attempts", entry.getAttempts()); + model.put( + "number", + PhoneNumberUtil.getInstance() + .format( + entry.getPhoneNumber(), + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + model.put("method", verificationMethod(request)); + return new ModelAndView(model, "enter_verify_number.ftl"); + }; + public static Route postVerifyNumber = + (request, response) -> { + final Session session = request.session(); + final Jid jid = getVerifiedJid(session); + if (jid == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + final Entry entry = Database.getInstance().getEntry(jid); + + if (entry == null) { + response.redirect("/enter/make-payment/?error=not-yet"); return null; } - if (entry.getAttempts() <= 0) { - response.redirect("/enter/send-number-verification/?error=attempts"); + if (entry.isVerified() && entry.getPhoneNumber() != null) { + response.redirect("/enter/finished/"); return null; } + String code = request.queryParams("code"); try { - VERIFICATION_PROVIDER.request(phoneNumber, method); + if (code == null + || code.trim().isEmpty() + || !VERIFICATION_PROVIDER.verify(entry.getPhoneNumber(), code.trim())) { + response.redirect("/enter/verify-number/?error=invalid"); + } else { + entry.setVerified(true); + Database.getInstance().updateEntry(entry); + response.redirect("/enter/finished/"); + } } catch (im.quicksy.server.verification.RequestFailedException e) { - if (e.getCode() == TwilioVerificationProvider.PHONE_NUMBER_IS_INVALID) { - LOGGER.info("verification provider said " + phoneNumber + " is invalid"); - response.redirect("/enter/send-number-verification/?error=invalid"); - return null; + if (e.getCode() == TwilioVerificationProvider.PHONE_VERIFICATION_NOT_FOUND) { + response.redirect("/enter/verify-number/?error=expired"); + } else { + LOGGER.error( + "problem verifying sms authentication token" + + e.getMessage() + + " code=" + + e.getCode()); + response.redirect("/enter/verify-number/?error=upstream"); } - LOGGER.warn("unable to send SMS verification message to " + phoneNumber + " (" + e.getMessage() + ", code=" + e.getCode() + ")"); - response.redirect("/enter/send-number-verification/?error=failed"); + } + return null; + }; + public static TemplateViewRoute getFinished = + (request, response) -> { + final Session session = request.session(); + final Jid jid = getVerifiedJid(session); + if (jid == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); return null; } - entry.setPhoneNumber(phoneNumber); - entry.setVerified(false); - entry.decreaseAttempts(); - Database.getInstance().updateEntry(entry); - response.redirect("/enter/verify-number/?method="+method.toString()); - } catch (NumberParseException e) { - response.redirect("/enter/send-number-verification/?error=invalid"); - } - } else { - response.redirect("/enter/send-number-verification/?error=invalid"); - } - return null; - }; - public static TemplateViewRoute getVerifyNumber = (request, response) -> { - final Session session = request.session(); - final Jid jid = getVerifiedJid(session); - if (jid == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - final HashMap<String, Object> model = new HashMap<>(); - - final Entry entry = Database.getInstance().getEntry(jid); - - if (entry == null) { - response.status(500); - return null; - } else if (entry.getPhoneNumber() == null) { - response.redirect("/enter/send-number-verification/"); - return null; - } - - final String error = request.queryParams("error"); - if (error != null) { - model.put("error", error); - } - model.put("attempts", entry.getAttempts()); - model.put("number", PhoneNumberUtil.getInstance().format(entry.getPhoneNumber(), PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); - model.put("method",verificationMethod(request)); - return new ModelAndView(model, "enter_verify_number.ftl"); - }; - public static Route postVerifyNumber = (request, response) -> { - final Session session = request.session(); - final Jid jid = getVerifiedJid(session); - if (jid == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - final Entry entry = Database.getInstance().getEntry(jid); - - if (entry == null) { - response.redirect("/enter/make-payment/?error=not-yet"); - return null; - } - - if (entry.isVerified() && entry.getPhoneNumber() != null) { - response.redirect("/enter/finished/"); - return null; - } + final HashMap<String, Object> model = new HashMap<>(); - String code = request.queryParams("code"); - try { - if (code == null || code.trim().isEmpty() || !VERIFICATION_PROVIDER.verify(entry.getPhoneNumber(), code.trim())) { - response.redirect("/enter/verify-number/?error=invalid"); - } else { - entry.setVerified(true); - Database.getInstance().updateEntry(entry); - response.redirect("/enter/finished/"); - } - } catch (im.quicksy.server.verification.RequestFailedException e) { - if (e.getCode() == TwilioVerificationProvider.PHONE_VERIFICATION_NOT_FOUND) { - response.redirect("/enter/verify-number/?error=expired"); - } else { - LOGGER.error("problem verifying sms authentication token"+e.getMessage()+" code="+e.getCode()); - response.redirect("/enter/verify-number/?error=upstream"); - } - } - return null; - }; - public static TemplateViewRoute getFinished = (request, response) -> { - final Session session = request.session(); - final Jid jid = getVerifiedJid(session); - if (jid == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - final HashMap<String, Object> model = new HashMap<>(); + final Entry entry = Database.getInstance().getEntry(jid); - final Entry entry = Database.getInstance().getEntry(jid); + session.removeAttribute("delete-confirmed"); + session.removeAttribute("reset-confirmed"); - session.removeAttribute("delete-confirmed"); - session.removeAttribute("reset-confirmed"); + if (entry == null || !entry.isVerified() || entry.getPhoneNumber() == null) { + response.status(500); + return null; + } - if (entry == null || !entry.isVerified() || entry.getPhoneNumber() == null) { - response.status(500); - return null; - } + model.put( + "number", + PhoneNumberUtil.getInstance() + .format( + entry.getPhoneNumber(), + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + model.put("jid", jid.toEscapedString()); + model.put("attempts", entry.getAttempts()); + return new ModelAndView(model, "enter_finished.ftl"); + }; + public static Route getReset = + (request, response) -> { + final Session session = request.session(); + final Jid jid = getVerifiedJid(session); + if (jid == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + Entry entry = Database.getInstance().getEntry(jid); + if (entry != null) { + Boolean resetConfirmed = session.attribute("reset-confirmed"); + if (entry.isVerified() && (resetConfirmed == null || !resetConfirmed)) { + response.redirect("/enter/finished/"); + } else if (entry.getAttempts() <= 0) { + response.redirect("/enter/verify-number/"); + } else { + entry.setPhoneNumber(null); + entry.setVerified(false); + Database.getInstance().updateEntry(entry); + VerificationProvider.Method method = verificationMethod(request); + response.redirect( + "/enter/send-number-verification/?method=" + method.toString()); + } + } else { + response.redirect("/enter/make-payment/"); + } + return null; + }; - model.put("number", PhoneNumberUtil.getInstance().format(entry.getPhoneNumber(), PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); - model.put("jid", jid.toEscapedString()); - model.put("attempts", entry.getAttempts()); - return new ModelAndView(model, "enter_finished.ftl"); - }; - public static Route getReset = (request, response) -> { - final Session session = request.session(); - final Jid jid = getVerifiedJid(session); - if (jid == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - Entry entry = Database.getInstance().getEntry(jid); - if (entry != null) { - Boolean resetConfirmed = session.attribute("reset-confirmed"); - if (entry.isVerified() && (resetConfirmed == null || !resetConfirmed)) { - response.redirect("/enter/finished/"); - } else if (entry.getAttempts() <= 0) { - response.redirect("/enter/verify-number/"); - } else { - entry.setPhoneNumber(null); - entry.setVerified(false); - Database.getInstance().updateEntry(entry); - VerificationProvider.Method method = verificationMethod(request); - response.redirect("/enter/send-number-verification/?method="+method.toString()); - } - } else { - response.redirect("/enter/make-payment/"); - } - return null; - }; + public static TemplateViewRoute getConfirmReset = + (request, response) -> { + final Session session = request.session(); + final Jid jid = getVerifiedJid(session); + if (jid == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + final HashMap<String, Object> model = new HashMap<>(); + Entry entry = Database.getInstance().getEntry(jid); + if (entry != null) { + if (entry.isVerified() && entry.getAttempts() <= 0) { + response.redirect("/enter/finished/"); + return null; + } else { + session.attribute("reset-confirmed", true); + } + } else { + response.redirect("/enter/make-payment/"); + return null; + } + return new ModelAndView(model, "confirm_reset.ftl"); + }; - public static TemplateViewRoute getConfirmReset = (request, response) -> { - final Session session = request.session(); - final Jid jid = getVerifiedJid(session); - if (jid == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - final HashMap<String, Object> model = new HashMap<>(); - Entry entry = Database.getInstance().getEntry(jid); - if (entry != null) { - if (entry.isVerified() && entry.getAttempts() <= 0) { - response.redirect("/enter/finished/"); - return null; - } else { - session.attribute("reset-confirmed",true); - } - } else { - response.redirect("/enter/make-payment/"); - return null; - } - return new ModelAndView(model, "confirm_reset.ftl"); - }; + public static TemplateViewRoute getConfirmDelete = + (request, response) -> { + final Session session = request.session(); + final Jid jid = getVerifiedJid(session); + if (jid == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + session.attribute("delete-confirmed", true); + return new ModelAndView(new HashMap<>(), "confirm_delete.ftl"); + }; - public static TemplateViewRoute getConfirmDelete = (request, response) -> { - final Session session = request.session(); - final Jid jid = getVerifiedJid(session); - if (jid == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - session.attribute("delete-confirmed",true); - return new ModelAndView(new HashMap<>(), "confirm_delete.ftl"); - }; + public static TemplateViewRoute getDelete = + (request, response) -> { + final Session session = request.session(); + final Jid jid = getVerifiedJid(session); + if (jid == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + final Boolean deleteConfirmed = session.attribute("delete-confirmed"); + final Entry entry = Database.getInstance().getEntry(jid); + if (deleteConfirmed == null || !deleteConfirmed) { + if (entry != null && entry.isVerified()) { + response.redirect("/enter/finished/"); + return null; + } else { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + } + if (entry == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + Database.getInstance().deleteEntry(entry); + session.invalidate(); + return new ModelAndView(new HashMap<>(), "deleted.ftl"); + }; - public static TemplateViewRoute getDelete = (request, response) -> { - final Session session = request.session(); - final Jid jid = getVerifiedJid(session); - if (jid == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - final Boolean deleteConfirmed = session.attribute("delete-confirmed"); - final Entry entry = Database.getInstance().getEntry(jid); - if (deleteConfirmed == null || !deleteConfirmed) { - if (entry != null && entry.isVerified()) { - response.redirect("/enter/finished/"); + public static Route getPayPalResult = + (request, response) -> { + final String uuid = request.params("uuid"); + final boolean success = "success".equals(request.params("status")); + String token = request.queryParams("token"); + String payerId = request.queryParams("PayerID"); + if (token == null || uuid == null || !success) { + response.redirect("/enter/make-payment/?error=failed"); + return null; + } + Payment payment = Database.getInstance().getPayment(uuid); + if (payment == null) { + response.redirect("/enter/make-payment/?error=cart"); + return null; + } + if (PayPal.doExpressCheckoutPayment(token, payerId, payment.getTotal())) { + if (Database.getInstance() + .updatePaymentAndCreateEntry(payment, new Entry(payment.getOwner()))) { + response.redirect("/enter/payment-received/"); + } else { + response.redirect("/enter/make-payment/?error=redeem-failed"); + } + } else { + response.redirect("/enter/make-payment/?error=failed"); + } return null; - } else { - response.redirect("/enter/send-jabber-verification/?error=cookies"); + }; + public static Route postCheckout = + (request, response) -> { + final Session session = request.session(); + final Jid jid = getVerifiedJid(session); + if (jid == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + Payment payment = new Payment(jid, PaymentMethod.PAYPAL); + final String token = PayPal.setExpressCheckout(payment); + payment.setToken(token); + Database.getInstance().createPayment(payment); + response.redirect(PayPal.getRedirectionUrl(token)); return null; - } - } - if (entry == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - Database.getInstance().deleteEntry(entry); - session.invalidate(); - return new ModelAndView(new HashMap<>(), "deleted.ftl"); - }; - - public static Route getPayPalResult = (request, response) -> { - final String uuid = request.params("uuid"); - final boolean success = "success".equals(request.params("status")); - String token = request.queryParams("token"); - String payerId = request.queryParams("PayerID"); - if (token == null || uuid == null || !success) { - response.redirect("/enter/make-payment/?error=failed"); - return null; - } - Payment payment = Database.getInstance().getPayment(uuid); - if (payment == null) { - response.redirect("/enter/make-payment/?error=cart"); - return null; - } - if (PayPal.doExpressCheckoutPayment(token, payerId, payment.getTotal())) { - if (Database.getInstance().updatePaymentAndCreateEntry(payment, new Entry(payment.getOwner()))) { - response.redirect("/enter/payment-received/"); - } else { - response.redirect("/enter/make-payment/?error=redeem-failed"); - } - } else { - response.redirect("/enter/make-payment/?error=failed"); - } - return null; - }; - public static Route postCheckout = (request, response) -> { - final Session session = request.session(); - final Jid jid = getVerifiedJid(session); - if (jid == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - Payment payment = new Payment(jid, PaymentMethod.PAYPAL); - final String token = PayPal.setExpressCheckout(payment); - payment.setToken(token); - Database.getInstance().createPayment(payment); - response.redirect(PayPal.getRedirectionUrl(token)); - return null; - }; - public static TemplateViewRoute getPaymentReceived = (request, response) -> { - final Session session = request.session(); - final Jid jid = getVerifiedJid(session); - if (jid == null) { - response.redirect("/enter/send-jabber-verification/?error=cookies"); - return null; - } - return new ModelAndView(null, "enter_payment_received.ftl"); - }; + }; + public static TemplateViewRoute getPaymentReceived = + (request, response) -> { + final Session session = request.session(); + final Jid jid = getVerifiedJid(session); + if (jid == null) { + response.redirect("/enter/send-jabber-verification/?error=cookies"); + return null; + } + return new ModelAndView(null, "enter_payment_received.ftl"); + }; private static Jid getVerifiedJid(Session session) { final Jid jid = session.attribute("jid"); diff --git a/src/main/java/im/quicksy/server/controller/PasswordController.java b/src/main/java/im/quicksy/server/controller/PasswordController.java index 9b4d486..16f26ba 100644 --- a/src/main/java/im/quicksy/server/controller/PasswordController.java +++ b/src/main/java/im/quicksy/server/controller/PasswordController.java @@ -16,125 +16,161 @@ package im.quicksy.server.controller; +import static spark.Spark.halt; + import com.google.common.base.Splitter; import com.google.common.io.BaseEncoding; import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.Phonenumber; import de.gultsch.ejabberd.api.results.Last; -import im.quicksy.server.configuration.Configuration; import im.quicksy.server.Utils; +import im.quicksy.server.configuration.Configuration; import im.quicksy.server.ejabberd.MyEjabberdApi; import im.quicksy.server.throttle.RateLimiter; import im.quicksy.server.throttle.Strategy; import im.quicksy.server.verification.RequestFailedException; import im.quicksy.server.verification.TokenExpiredException; -import im.quicksy.server.verification.TwilioVerificationProvider; +import java.net.InetAddress; +import java.time.Duration; +import java.time.Instant; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import rocks.xmpp.addr.Jid; import spark.Filter; import spark.Route; -import java.net.InetAddress; -import java.time.Duration; -import java.time.Instant; -import java.util.List; - -import static spark.Spark.halt; - public class PasswordController extends BaseController { private static final Logger LOGGER = LoggerFactory.getLogger(PasswordController.class); - private static final RateLimiter<InetAddress> PER_IP_RATE_LIMITER = RateLimiter.of( - Strategy.of(Duration.ofMinutes(5), 16) - ); + private static final RateLimiter<InetAddress> PER_IP_RATE_LIMITER = + RateLimiter.of(Strategy.of(Duration.ofMinutes(5), 16)); - public static Route setPassword = (request, response) -> { - final String authorization = request.headers(HEADER_AUTHORIZATION); - final String password = request.body(); - if (authorization == null) { - return halt(401); - } - if (password == null) { - return halt(400, "Missing password"); - } - final String username; - final String pin; - try { - List<String> parameters = Splitter.on('\u0000').omitEmptyStrings().limit(2).splitToList(new String(BaseEncoding.base64().decode(authorization))); - if (parameters.size() != 2) { - throw new IllegalArgumentException("Invalid number of authorization parameters. Was " + parameters.size()); - } - username = parameters.get(0); - pin = parameters.get(1); - } catch (Exception e) { - return halt(400, "Unable to parse authorization"); - } - if (E164_PATTERN.matcher(username).matches() && PIN_PATTERN.matcher(pin).matches()) { - final Phonenumber.PhoneNumber phonenumber; - try { - phonenumber = PhoneNumberUtil.getInstance().parse(username, "de"); - } catch (NumberParseException e) { - return halt(400, "Unable to parse phone number"); - } + public static Route setPassword = + (request, response) -> { + final String authorization = request.headers(HEADER_AUTHORIZATION); + final String password = request.body(); + if (authorization == null) { + return halt(401); + } + if (password == null) { + return halt(400, "Missing password"); + } + final String username; + final String pin; + try { + List<String> parameters = + Splitter.on('\u0000') + .omitEmptyStrings() + .limit(2) + .splitToList( + new String( + BaseEncoding.base64().decode(authorization))); + if (parameters.size() != 2) { + throw new IllegalArgumentException( + "Invalid number of authorization parameters. Was " + + parameters.size()); + } + username = parameters.get(0); + pin = parameters.get(1); + } catch (Exception e) { + return halt(400, "Unable to parse authorization"); + } + if (E164_PATTERN.matcher(username).matches() + && PIN_PATTERN.matcher(pin).matches()) { + final Phonenumber.PhoneNumber phonenumber; + try { + phonenumber = PhoneNumberUtil.getInstance().parse(username, "de"); + } catch (NumberParseException e) { + return halt(400, "Unable to parse phone number"); + } - final Jid jid = Utils.jidOf(phonenumber); + final Jid jid = Utils.jidOf(phonenumber); - if (Configuration.getInstance().isPreventRegistration() && MyEjabberdApi.getInstance().getUserResources(jid.getEscapedLocal(), jid.getDomain()).size() > 0) { - return halt(409); - } + if (Configuration.getInstance().isPreventRegistration() + && MyEjabberdApi.getInstance() + .getUserResources( + jid.getEscapedLocal(), jid.getDomain()) + .size() + > 0) { + return halt(409); + } - try { - if (VERIFICATION_PROVIDER.verify(phonenumber, pin)) { - if (MyEjabberdApi.getInstance().checkAccount(jid.getEscapedLocal(), jid.getDomain())) { - final Last last = MyEjabberdApi.getInstance().getLast(jid.getEscapedLocal(), jid.getDomain()); - final Duration lastActivity = Duration.between(last.getTimestamp(), Instant.now()); - LOGGER.info("user " + jid.getEscapedLocal() + " was last active " + lastActivity + " ago."); - if (Configuration.getInstance().getAccountInactivity().minus(lastActivity).isNegative()) { - LOGGER.info("delete old and create new user " + jid); - MyEjabberdApi.getInstance().unregister(jid.getEscapedLocal(), jid.getDomain()); - MyEjabberdApi.getInstance().register(jid.getEscapedLocal(), jid.getDomain(), password); - response.status(201); + try { + if (VERIFICATION_PROVIDER.verify(phonenumber, pin)) { + if (MyEjabberdApi.getInstance() + .checkAccount(jid.getEscapedLocal(), jid.getDomain())) { + final Last last = + MyEjabberdApi.getInstance() + .getLast(jid.getEscapedLocal(), jid.getDomain()); + final Duration lastActivity = + Duration.between(last.getTimestamp(), Instant.now()); + LOGGER.info( + "user " + + jid.getEscapedLocal() + + " was last active " + + lastActivity + + " ago."); + if (Configuration.getInstance() + .getAccountInactivity() + .minus(lastActivity) + .isNegative()) { + LOGGER.info("delete old and create new user " + jid); + MyEjabberdApi.getInstance() + .unregister(jid.getEscapedLocal(), jid.getDomain()); + MyEjabberdApi.getInstance() + .register( + jid.getEscapedLocal(), + jid.getDomain(), + password); + response.status(201); + } else { + LOGGER.info("changing password for existing user " + jid); + MyEjabberdApi.getInstance() + .changePassword( + jid.getEscapedLocal(), + jid.getDomain(), + password); + } + } else { + LOGGER.info("create new user " + jid); + MyEjabberdApi.getInstance() + .register(jid.getEscapedLocal(), jid.getDomain(), password); + response.status(201); + } + return ""; } else { - LOGGER.info("changing password for existing user " + jid); - MyEjabberdApi.getInstance().changePassword(jid.getEscapedLocal(), jid.getDomain(), password); + LOGGER.info("verification provider reported wrong pin"); + return halt(401); } - } else { - LOGGER.info("create new user " + jid); - MyEjabberdApi.getInstance().register(jid.getEscapedLocal(), jid.getDomain(), password); - response.status(201); + } catch (TokenExpiredException e) { + LOGGER.warn( + "Contacting verification provider failed with: " + e.getMessage()); + return halt(404); + } catch (RequestFailedException e) { + LOGGER.warn( + "Contacting verification provider failed with: " + e.getMessage()); + return halt(500); + } catch (de.gultsch.ejabberd.api.RequestFailedException e) { + LOGGER.warn("Contacting ejabberd failed with: " + e.getMessage()); + return halt(500); } - return ""; } else { - LOGGER.info("verification provider reported wrong pin"); - return halt(401); + System.out.println("pattern didnt match"); + return halt(400); } - } catch (TokenExpiredException e) { - LOGGER.warn("Contacting verification provider failed with: " + e.getMessage()); - return halt(404); - } catch (RequestFailedException e) { - LOGGER.warn("Contacting verification provider failed with: " + e.getMessage()); - return halt(500); - } catch (de.gultsch.ejabberd.api.RequestFailedException e) { - LOGGER.warn("Contacting ejabberd failed with: " + e.getMessage()); - return halt(500); - } - } else { - System.out.println("pattern didnt match"); - return halt(400); - } - }; - - public static Filter throttleIp = (request, response) -> { - try { - PER_IP_RATE_LIMITER.attempt(getClientIp(request)); - } catch (RateLimiter.RetryInException e) { - LOGGER.info(e.getMessage()); - response.header("Retry-After", String.valueOf(e.getInterval().getSeconds())); - halt(429, e.getMessage()); - } - }; + }; + public static Filter throttleIp = + (request, response) -> { + try { + PER_IP_RATE_LIMITER.attempt(getClientIp(request)); + } catch (RateLimiter.RetryInException e) { + LOGGER.info(e.getMessage()); + response.header("Retry-After", String.valueOf(e.getInterval().getSeconds())); + halt(429, e.getMessage()); + } + }; } diff --git a/src/main/java/im/quicksy/server/controller/SynchronizationController.java b/src/main/java/im/quicksy/server/controller/SynchronizationController.java index 2c68109..8f2633d 100644 --- a/src/main/java/im/quicksy/server/controller/SynchronizationController.java +++ b/src/main/java/im/quicksy/server/controller/SynchronizationController.java @@ -22,6 +22,10 @@ import im.quicksy.server.throttle.Strategy; import im.quicksy.server.throttle.VolumeLimiter; import im.quicksy.server.xmpp.synchronization.Entry; import im.quicksy.server.xmpp.synchronization.PhoneBook; +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import rocks.xmpp.addr.Jid; @@ -29,59 +33,62 @@ import rocks.xmpp.core.stanza.IQHandler; import rocks.xmpp.core.stanza.model.StanzaError; import rocks.xmpp.core.stanza.model.errors.Condition; -import java.time.Duration; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - public class SynchronizationController { private static final Logger LOGGER = LoggerFactory.getLogger(SynchronizationController.class); - private static final VolumeLimiter<Jid, String> PHONE_NUMBER_LIMITER = new VolumeLimiter<>( - Strategy.of(Duration.ofDays(1), 2048) - ); + private static final VolumeLimiter<Jid, String> PHONE_NUMBER_LIMITER = + new VolumeLimiter<>(Strategy.of(Duration.ofDays(1), 2048)); - public static IQHandler synchronize = iq -> { - final PhoneBook phoneBook = iq.getExtension(PhoneBook.class); - final Jid user = iq.getFrom().asBareJid(); - if (phoneBook == null) { - return iq.createError(Condition.BAD_REQUEST); - } - final String domain = Configuration.getInstance().getDomain(); + public static IQHandler synchronize = + iq -> { + final PhoneBook phoneBook = iq.getExtension(PhoneBook.class); + final Jid user = iq.getFrom().asBareJid(); + if (phoneBook == null) { + return iq.createError(Condition.BAD_REQUEST); + } + final String domain = Configuration.getInstance().getDomain(); - if (!user.getDomain().equals(domain)) { - return iq.createError(Condition.NOT_AUTHORIZED); - } + if (!user.getDomain().equals(domain)) { + return iq.createError(Condition.NOT_AUTHORIZED); + } - final List<String> phoneNumbers = phoneBook.getPhoneNumbers(); + final List<String> phoneNumbers = phoneBook.getPhoneNumbers(); - try { - PHONE_NUMBER_LIMITER.attempt(user, phoneNumbers); - } catch (VolumeLimiter.RetryInException e) { - return iq.createError(new StanzaError(Condition.POLICY_VIOLATION, e.getMessage())); - } + try { + PHONE_NUMBER_LIMITER.attempt(user, phoneNumbers); + } catch (VolumeLimiter.RetryInException e) { + return iq.createError( + new StanzaError(Condition.POLICY_VIOLATION, e.getMessage())); + } - LOGGER.info(user + " requested to sync " + phoneNumbers.size() + " phone numbers"); - final HashMap<String, Entry> entryMap = new HashMap<>(); - final List<String> existingUsersOnQuicksy = Database.getInstance().findExistingUsers(domain, phoneNumbers); - for (String phoneNumber : existingUsersOnQuicksy) { - Entry entry = entryMap.computeIfAbsent(phoneNumber, Entry::new); - entry.addJid(Jid.of(phoneNumber, domain, null)); - } - final List<Database.RawEntry> directoryUsers = Database.getInstance().findDirectoryUsers(phoneNumbers); - for (Database.RawEntry rawEntry : directoryUsers) { - Entry entry = entryMap.computeIfAbsent(rawEntry.getPhoneNumber(), Entry::new); - entry.addJid(rawEntry.getJid()); - } - final List<Entry> entries = new ArrayList<>(entryMap.values()); - final String hash = Entry.statusQuo(entries); - if (hash.equals(phoneBook.getVer())) { - LOGGER.info("hash hasn't changed for " + user + " (" + entries.size() + " entries)"); - return iq.createResult(); - } - ; - LOGGER.info("responding to " + user + " with " + entries.size() + " entries"); - return iq.createResult(new PhoneBook(entries)); - }; + LOGGER.info(user + " requested to sync " + phoneNumbers.size() + " phone numbers"); + final HashMap<String, Entry> entryMap = new HashMap<>(); + final List<String> existingUsersOnQuicksy = + Database.getInstance().findExistingUsers(domain, phoneNumbers); + for (String phoneNumber : existingUsersOnQuicksy) { + Entry entry = entryMap.computeIfAbsent(phoneNumber, Entry::new); + entry.addJid(Jid.of(phoneNumber, domain, null)); + } + final List<Database.RawEntry> directoryUsers = + Database.getInstance().findDirectoryUsers(phoneNumbers); + for (Database.RawEntry rawEntry : directoryUsers) { + Entry entry = entryMap.computeIfAbsent(rawEntry.getPhoneNumber(), Entry::new); + entry.addJid(rawEntry.getJid()); + } + final List<Entry> entries = new ArrayList<>(entryMap.values()); + final String hash = Entry.statusQuo(entries); + if (hash.equals(phoneBook.getVer())) { + LOGGER.info( + "hash hasn't changed for " + + user + + " (" + + entries.size() + + " entries)"); + return iq.createResult(); + } + ; + LOGGER.info("responding to " + user + " with " + entries.size() + " entries"); + return iq.createResult(new PhoneBook(entries)); + }; } diff --git a/src/main/java/im/quicksy/server/database/Database.java b/src/main/java/im/quicksy/server/database/Database.java index 7decda6..00063fd 100644 --- a/src/main/java/im/quicksy/server/database/Database.java +++ b/src/main/java/im/quicksy/server/database/Database.java @@ -16,15 +16,21 @@ package im.quicksy.server.database; +import static im.quicksy.server.database.SqlQuery.*; + import com.google.i18n.phonenumbers.Phonenumber; import com.zaxxer.hikari.HikariDataSource; import de.gultsch.xmpp.addr.adapter.Adapter; import im.quicksy.server.configuration.Configuration; -import im.quicksy.server.configuration.DatabaseConfigurationBundle; import im.quicksy.server.configuration.DatabaseConfiguration; +import im.quicksy.server.configuration.DatabaseConfigurationBundle; import im.quicksy.server.pojo.Entry; import im.quicksy.server.pojo.Payment; import im.quicksy.server.pojo.PaymentStatus; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sql2o.Connection; @@ -34,13 +40,6 @@ import org.sql2o.quirks.NoQuirks; import org.sql2o.quirks.Quirks; import rocks.xmpp.addr.Jid; -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.List; -import java.util.UUID; - -import static im.quicksy.server.database.SqlQuery.*; - public class Database { private static final Logger LOGGER = LoggerFactory.getLogger(Database.class); @@ -68,9 +67,22 @@ public class Database { } private static void setup(Sql2o quicksyDatabase) { - try(Connection connection = quicksyDatabase.open()){ - connection.createQuery("CREATE TABLE IF NOT EXISTS `payments` ( `uuid` char(36) NOT NULL, `owner` varchar(191) DEFAULT NULL, `method` varchar(100) DEFAULT NULL, `token` varchar(255) DEFAULT NULL, `total` float DEFAULT NULL, `status` varchar(100) DEFAULT NULL, `created` timestamp NOT NULL, PRIMARY KEY (`uuid`))").executeUpdate(); - connection.createQuery("CREATE TABLE IF NOT EXISTS `entries` ( `jid` varchar(191) NOT NULL, `phoneNumber` varchar(25) DEFAULT NULL, `verified` tinyint(1) DEFAULT '0', `attempts` int(11) DEFAULT NULL, PRIMARY KEY (`jid`))").executeUpdate(); + try (Connection connection = quicksyDatabase.open()) { + connection + .createQuery( + "CREATE TABLE IF NOT EXISTS `payments` ( `uuid` char(36) NOT NULL," + + " `owner` varchar(191) DEFAULT NULL, `method` varchar(100)" + + " DEFAULT NULL, `token` varchar(255) DEFAULT NULL, `total` float" + + " DEFAULT NULL, `status` varchar(100) DEFAULT NULL, `created`" + + " timestamp NOT NULL, PRIMARY KEY (`uuid`))") + .executeUpdate(); + connection + .createQuery( + "CREATE TABLE IF NOT EXISTS `entries` ( `jid` varchar(191) NOT NULL," + + " `phoneNumber` varchar(25) DEFAULT NULL, `verified` tinyint(1)" + + " DEFAULT '0', `attempts` int(11) DEFAULT NULL, PRIMARY KEY" + + " (`jid`))") + .executeUpdate(); } } @@ -83,7 +95,7 @@ public class Database { return new Sql2o(dataSource, QUIRKS); } - public static synchronized Database getInstance() { + public static synchronized Database getInstance() { if (INSTANCE == null) { INSTANCE = new Database(Configuration.getInstance().getDatabaseConfigurationBundle()); } @@ -92,7 +104,8 @@ public class Database { public List<String> findExistingUsers(String host, List<String> users) { try (Connection connection = this.ejabberdDatabase.open()) { - return connection.createQuery(FIND_EXISTING_USERS) + return connection + .createQuery(FIND_EXISTING_USERS) .addParameter("host", host) .addParameter("users", users) .executeAndFetch(String.class); @@ -101,7 +114,8 @@ public class Database { public List<RawEntry> findDirectoryUsers(List<String> phoneNumbers) { try (Connection connection = this.quicksyDatabase.open()) { - return connection.createQuery(FIND_DICTIONARY_ENTRIES) + return connection + .createQuery(FIND_DICTIONARY_ENTRIES) .addParameter("phoneNumbers", phoneNumbers) .executeAndFetch(RawEntry.class); } @@ -109,7 +123,10 @@ public class Database { public Entry getEntry(Jid jid) { try (Connection connection = this.quicksyDatabase.open()) { - return connection.createQuery(GET_ENTRY).addParameter("jid", jid).executeAndFetchFirst(Entry.class); + return connection + .createQuery(GET_ENTRY) + .addParameter("jid", jid) + .executeAndFetchFirst(Entry.class); } } @@ -127,7 +144,10 @@ public class Database { public Payment getPayment(String uuid) { try (Connection connection = this.quicksyDatabase.open()) { - return connection.createQuery(GET_PAYMENT).addParameter("uuid", uuid).executeAndFetchFirst(Payment.class); + return connection + .createQuery(GET_PAYMENT) + .addParameter("uuid", uuid) + .executeAndFetchFirst(Payment.class); } } @@ -138,16 +158,26 @@ public class Database { } public boolean updatePaymentAndCreateEntry(Payment payment, Entry entry) { - try (Connection connection = this.quicksyDatabase.beginTransaction(java.sql.Connection.TRANSACTION_SERIALIZABLE)) { + try (Connection connection = + this.quicksyDatabase.beginTransaction( + java.sql.Connection.TRANSACTION_SERIALIZABLE)) { connection.setRollbackOnException(true); connection.createQuery(CREATE_ENTRY).bind(entry).executeUpdate(); - int count = connection.createQuery(MAKE_PAYMENT) - .addParameter("status", PaymentStatus.PAYED) - .addParameter("expectedStatus",PaymentStatus.PENDING) - .addParameter("uuid", UUID.class, payment.getUuid()) //uuid somehow needs to be called with class to run through the converter - .addParameter("token", payment.getToken()).executeUpdate().getResult(); + int count = + connection + .createQuery(MAKE_PAYMENT) + .addParameter("status", PaymentStatus.PAYED) + .addParameter("expectedStatus", PaymentStatus.PENDING) + .addParameter( + "uuid", + UUID.class, + payment.getUuid()) // uuid somehow needs to be called with + // class to run through the converter + .addParameter("token", payment.getToken()) + .executeUpdate() + .getResult(); if (count != 1) { - throw new Exception("Unable to make payment. updated row count was "+count); + throw new Exception("Unable to make payment. updated row count was " + count); } connection.commit(); } catch (Exception e) { diff --git a/src/main/java/im/quicksy/server/database/LocalDateTimeConverter.java b/src/main/java/im/quicksy/server/database/LocalDateTimeConverter.java index a0e107a..f1fb66a 100644 --- a/src/main/java/im/quicksy/server/database/LocalDateTimeConverter.java +++ b/src/main/java/im/quicksy/server/database/LocalDateTimeConverter.java @@ -16,11 +16,10 @@ package im.quicksy.server.database; -import org.sql2o.converters.Converter; -import org.sql2o.converters.ConverterException; - import java.sql.Timestamp; import java.time.LocalDateTime; +import org.sql2o.converters.Converter; +import org.sql2o.converters.ConverterException; public class LocalDateTimeConverter implements Converter<LocalDateTime> { @@ -29,7 +28,10 @@ public class LocalDateTimeConverter implements Converter<LocalDateTime> { if (o instanceof Timestamp) { return ((Timestamp) o).toLocalDateTime(); } else { - throw new ConverterException("can not convert object of type " + o.getClass().getName() + " to LocalDateTime"); + throw new ConverterException( + "can not convert object of type " + + o.getClass().getName() + + " to LocalDateTime"); } } @@ -37,4 +39,4 @@ public class LocalDateTimeConverter implements Converter<LocalDateTime> { public Object toDatabaseParam(LocalDateTime localDateTime) { return Timestamp.valueOf(localDateTime); } -} \ No newline at end of file +} diff --git a/src/main/java/im/quicksy/server/database/PhoneNumberConverter.java b/src/main/java/im/quicksy/server/database/PhoneNumberConverter.java index a545477..dd93f1b 100644 --- a/src/main/java/im/quicksy/server/database/PhoneNumberConverter.java +++ b/src/main/java/im/quicksy/server/database/PhoneNumberConverter.java @@ -19,11 +19,10 @@ package im.quicksy.server.database; import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; +import java.util.regex.Pattern; import org.sql2o.converters.Converter; import org.sql2o.converters.ConverterException; -import java.util.regex.Pattern; - public class PhoneNumberConverter implements Converter<PhoneNumber> { public static Pattern E164_PATTERN = Pattern.compile("^\\+?[1-9]\\d{1,14}$"); @@ -39,7 +38,7 @@ public class PhoneNumberConverter implements Converter<PhoneNumber> { throw new ConverterException("String doesn't match e164 pattern"); } try { - return PhoneNumberUtil.getInstance().parse(string,"us"); + return PhoneNumberUtil.getInstance().parse(string, "us"); } catch (NumberParseException e) { throw new ConverterException(e.getMessage()); } @@ -50,6 +49,7 @@ public class PhoneNumberConverter implements Converter<PhoneNumber> { @Override public Object toDatabaseParam(PhoneNumber phoneNumber) { - return PhoneNumberUtil.getInstance().format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164); + return PhoneNumberUtil.getInstance() + .format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164); } } diff --git a/src/main/java/im/quicksy/server/database/SqlQuery.java b/src/main/java/im/quicksy/server/database/SqlQuery.java index 9add2e6..ac31966 100644 --- a/src/main/java/im/quicksy/server/database/SqlQuery.java +++ b/src/main/java/im/quicksy/server/database/SqlQuery.java @@ -18,16 +18,29 @@ package im.quicksy.server.database; public final class SqlQuery { - static final String FIND_EXISTING_USERS = "select username from users where server_host=:host and username in(:users)"; - static final String FIND_DICTIONARY_ENTRIES = "select jid,phoneNumber from entries where phoneNumber in(:phoneNumbers) and verified=1"; + static final String FIND_EXISTING_USERS = + "select username from users where server_host=:host and username in(:users)"; + static final String FIND_DICTIONARY_ENTRIES = + "select jid,phoneNumber from entries where phoneNumber in(:phoneNumbers) and" + + " verified=1"; - static final String GET_ENTRY = "SELECT jid,phoneNumber,verified,attempts FROM entries where jid=:jid limit 1"; - static final String CREATE_ENTRY = "insert into entries(jid,phoneNumber,verified,attempts) values(:jid,:phoneNumber,:verified,:attempts)"; - static final String UPDATE_ENTRY = "update entries set phoneNumber=:phoneNumber, verified=:verified, attempts=:attempts where jid=:jid"; + static final String GET_ENTRY = + "SELECT jid,phoneNumber,verified,attempts FROM entries where jid=:jid limit 1"; + static final String CREATE_ENTRY = + "insert into entries(jid,phoneNumber,verified,attempts)" + + " values(:jid,:phoneNumber,:verified,:attempts)"; + static final String UPDATE_ENTRY = + "update entries set phoneNumber=:phoneNumber, verified=:verified, attempts=:attempts" + + " where jid=:jid"; static final String DELETE_ENTRY = "delete from entries where jid=:jid limit 1"; - static final String GET_PAYMENT = "select uuid,owner,method,token,total,status,created from payments where uuid=:uuid limit 1"; - static final String CREATE_PAYMENT = "insert into payments(uuid,owner,method,token,total,status,created) values(:uuid,:owner,:method,:token,:total,:status,:created)"; - static final String MAKE_PAYMENT = "update payments set token=:token,status=:status where uuid=:uuid and status=:expectedStatus"; - + static final String GET_PAYMENT = + "select uuid,owner,method,token,total,status,created from payments where uuid=:uuid" + + " limit 1"; + static final String CREATE_PAYMENT = + "insert into payments(uuid,owner,method,token,total,status,created)" + + " values(:uuid,:owner,:method,:token,:total,:status,:created)"; + static final String MAKE_PAYMENT = + "update payments set token=:token,status=:status where uuid=:uuid and" + + " status=:expectedStatus"; } diff --git a/src/main/java/im/quicksy/server/database/UUIDStringConverter.java b/src/main/java/im/quicksy/server/database/UUIDStringConverter.java index 8cceda9..39c9b51 100644 --- a/src/main/java/im/quicksy/server/database/UUIDStringConverter.java +++ b/src/main/java/im/quicksy/server/database/UUIDStringConverter.java @@ -16,11 +16,10 @@ package im.quicksy.server.database; +import java.util.UUID; import org.sql2o.converters.Converter; import org.sql2o.converters.ConverterException; -import java.util.UUID; - public class UUIDStringConverter implements Converter<UUID> { @Override public UUID convert(Object o) throws ConverterException { @@ -33,7 +32,8 @@ public class UUIDStringConverter implements Converter<UUID> { throw new ConverterException("uuid string is not properly formatted"); } } else { - throw new ConverterException("can not convert object of type " + o.getClass().getName() + " to UUID"); + throw new ConverterException( + "can not convert object of type " + o.getClass().getName() + " to UUID"); } } @@ -41,4 +41,4 @@ public class UUIDStringConverter implements Converter<UUID> { public Object toDatabaseParam(UUID uuid) { return uuid.toString(); } -} \ No newline at end of file +} diff --git a/src/main/java/im/quicksy/server/ejabberd/MyEjabberdApi.java b/src/main/java/im/quicksy/server/ejabberd/MyEjabberdApi.java index 57e45fc..a954144 100644 --- a/src/main/java/im/quicksy/server/ejabberd/MyEjabberdApi.java +++ b/src/main/java/im/quicksy/server/ejabberd/MyEjabberdApi.java @@ -29,5 +29,4 @@ public class MyEjabberdApi { public static Ejabberd getInstance() { return EJABBERD; } - } diff --git a/src/main/java/im/quicksy/server/json/DurationDeserializer.java b/src/main/java/im/quicksy/server/json/DurationDeserializer.java index 68ae0b7..d760126 100644 --- a/src/main/java/im/quicksy/server/json/DurationDeserializer.java +++ b/src/main/java/im/quicksy/server/json/DurationDeserializer.java @@ -20,14 +20,17 @@ import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; - import java.lang.reflect.Type; import java.time.Duration; import java.time.format.DateTimeParseException; public class DurationDeserializer implements JsonDeserializer<Duration> { @Override - public Duration deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + public Duration deserialize( + JsonElement jsonElement, + Type type, + JsonDeserializationContext jsonDeserializationContext) + throws JsonParseException { try { return Duration.parse(jsonElement.getAsString()); } catch (DateTimeParseException e) { diff --git a/src/main/java/im/quicksy/server/json/PatternDeserializer.java b/src/main/java/im/quicksy/server/json/PatternDeserializer.java index cfc067c..1d2e2e2 100644 --- a/src/main/java/im/quicksy/server/json/PatternDeserializer.java +++ b/src/main/java/im/quicksy/server/json/PatternDeserializer.java @@ -4,7 +4,6 @@ import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; - import java.lang.reflect.Type; import java.util.regex.Pattern; diff --git a/src/main/java/im/quicksy/server/json/VersionDeserializer.java b/src/main/java/im/quicksy/server/json/VersionDeserializer.java index adbdfa5..5484d97 100644 --- a/src/main/java/im/quicksy/server/json/VersionDeserializer.java +++ b/src/main/java/im/quicksy/server/json/VersionDeserializer.java @@ -22,12 +22,15 @@ import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; - import java.lang.reflect.Type; public class VersionDeserializer implements JsonDeserializer<Version> { @Override - public Version deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + public Version deserialize( + JsonElement jsonElement, + Type type, + JsonDeserializationContext jsonDeserializationContext) + throws JsonParseException { try { return Version.valueOf(jsonElement.getAsString()); } catch (ParseException e) { diff --git a/src/main/java/im/quicksy/server/pojo/Device.java b/src/main/java/im/quicksy/server/pojo/Device.java index c3423dd..800a272 100644 --- a/src/main/java/im/quicksy/server/pojo/Device.java +++ b/src/main/java/im/quicksy/server/pojo/Device.java @@ -43,5 +43,4 @@ public class Device { Device device = (Device) o; return Objects.equal(id, device.id); } - } diff --git a/src/main/java/im/quicksy/server/pojo/Entry.java b/src/main/java/im/quicksy/server/pojo/Entry.java index 576c890..988d3c7 100644 --- a/src/main/java/im/quicksy/server/pojo/Entry.java +++ b/src/main/java/im/quicksy/server/pojo/Entry.java @@ -21,7 +21,6 @@ import rocks.xmpp.addr.Jid; public class Entry { - private static final int DEFAULT_ATTEMPTS = 3; private Jid jid; diff --git a/src/main/java/im/quicksy/server/pojo/Payment.java b/src/main/java/im/quicksy/server/pojo/Payment.java index 2a520d8..71f9b0b 100644 --- a/src/main/java/im/quicksy/server/pojo/Payment.java +++ b/src/main/java/im/quicksy/server/pojo/Payment.java @@ -16,12 +16,11 @@ package im.quicksy.server.pojo; -import rocks.xmpp.addr.Jid; - import java.time.LocalDateTime; import java.util.Collections; import java.util.List; import java.util.UUID; +import rocks.xmpp.addr.Jid; public class Payment { @@ -35,7 +34,6 @@ public class Payment { private PaymentStatus status; private LocalDateTime created; - public Payment(Jid jid, PaymentMethod method) { this.uuid = UUID.randomUUID(); this.owner = jid; diff --git a/src/main/java/im/quicksy/server/pojo/PaymentMethod.java b/src/main/java/im/quicksy/server/pojo/PaymentMethod.java index 8360601..5bc498d 100644 --- a/src/main/java/im/quicksy/server/pojo/PaymentMethod.java +++ b/src/main/java/im/quicksy/server/pojo/PaymentMethod.java @@ -17,5 +17,7 @@ package im.quicksy.server.pojo; public enum PaymentMethod { - PAYPAL, WIRE_TRANSFER, VOUCHER; + PAYPAL, + WIRE_TRANSFER, + VOUCHER; } diff --git a/src/main/java/im/quicksy/server/pojo/PaymentStatus.java b/src/main/java/im/quicksy/server/pojo/PaymentStatus.java index 42c063e..cbb1d9b 100644 --- a/src/main/java/im/quicksy/server/pojo/PaymentStatus.java +++ b/src/main/java/im/quicksy/server/pojo/PaymentStatus.java @@ -17,5 +17,6 @@ package im.quicksy.server.pojo; public enum PaymentStatus { - PENDING, PAYED + PENDING, + PAYED } diff --git a/src/main/java/im/quicksy/server/pojo/ShoppingCartItem.java b/src/main/java/im/quicksy/server/pojo/ShoppingCartItem.java index 7ef0f67..01052cd 100644 --- a/src/main/java/im/quicksy/server/pojo/ShoppingCartItem.java +++ b/src/main/java/im/quicksy/server/pojo/ShoppingCartItem.java @@ -33,5 +33,4 @@ public class ShoppingCartItem { public float getPrice() { return price; } - } diff --git a/src/main/java/im/quicksy/server/pojo/Voucher.java b/src/main/java/im/quicksy/server/pojo/Voucher.java index b911edf..19756f6 100644 --- a/src/main/java/im/quicksy/server/pojo/Voucher.java +++ b/src/main/java/im/quicksy/server/pojo/Voucher.java @@ -20,13 +20,12 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import im.quicksy.server.configuration.Configuration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.FileReader; import java.time.Instant; import java.util.Date; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Voucher { @@ -44,9 +43,12 @@ public class Voucher { public static boolean checkVoucher(String code) { try { final Gson gson = new GsonBuilder().create(); - List<Voucher> vouchers = gson.fromJson(new FileReader(Configuration.getInstance().getVoucherFile()),new TypeToken<List<Voucher>>(){}.getType()); - LOGGER.info(vouchers.size()+" vouchers on file"); - for(Voucher voucher : vouchers) { + List<Voucher> vouchers = + gson.fromJson( + new FileReader(Configuration.getInstance().getVoucherFile()), + new TypeToken<List<Voucher>>() {}.getType()); + LOGGER.info(vouchers.size() + " vouchers on file"); + for (Voucher voucher : vouchers) { if (code.equals(voucher.code) && voucher.isCurrentlyValid()) { return true; } @@ -57,5 +59,4 @@ public class Voucher { return false; } } - } diff --git a/src/main/java/im/quicksy/server/throttle/RateLimiter.java b/src/main/java/im/quicksy/server/throttle/RateLimiter.java index 8798d5b..3383fda 100644 --- a/src/main/java/im/quicksy/server/throttle/RateLimiter.java +++ b/src/main/java/im/quicksy/server/throttle/RateLimiter.java @@ -38,14 +38,15 @@ public class RateLimiter<T> { final long now = System.nanoTime(); check(entity, now); for (Strategy strategy : strategies) { - storage.computeIfAbsent(strategy, k -> new LinkedList<>()).addLast(Attempt.create(entity, strategy.getDuration().toNanos() + now)); + storage.computeIfAbsent(strategy, k -> new LinkedList<>()) + .addLast(Attempt.create(entity, strategy.getDuration().toNanos() + now)); } - } private void check(T entity, final long now) throws RetryInException { for (Strategy strategy : strategies) { - final LinkedList<Attempt<T>> list = storage.computeIfAbsent(strategy, k -> new LinkedList<>()); + final LinkedList<Attempt<T>> list = + storage.computeIfAbsent(strategy, k -> new LinkedList<>()); final Iterator<Attempt<T>> iterator = list.iterator(); int entityCount = 0; Attempt<T> firstAttempt = null; @@ -65,7 +66,6 @@ public class RateLimiter<T> { } } } - } } @@ -88,8 +88,12 @@ public class RateLimiter<T> { @Override public String getMessage() { - return "Throttled "+getEntityType()+"("+this.object.toString()+"). Retry in "+duration.toString(); + return "Throttled " + + getEntityType() + + "(" + + this.object.toString() + + "). Retry in " + + duration.toString(); } } - } diff --git a/src/main/java/im/quicksy/server/throttle/Strategy.java b/src/main/java/im/quicksy/server/throttle/Strategy.java index 759bb28..4779516 100644 --- a/src/main/java/im/quicksy/server/throttle/Strategy.java +++ b/src/main/java/im/quicksy/server/throttle/Strategy.java @@ -34,8 +34,7 @@ public class Strategy { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Strategy strategy = (Strategy) o; - return attempts == strategy.attempts && - Objects.equals(duration, strategy.duration); + return attempts == strategy.attempts && Objects.equals(duration, strategy.duration); } @Override diff --git a/src/main/java/im/quicksy/server/throttle/VolumeAttempt.java b/src/main/java/im/quicksy/server/throttle/VolumeAttempt.java index ff6bf0d..95ba09b 100644 --- a/src/main/java/im/quicksy/server/throttle/VolumeAttempt.java +++ b/src/main/java/im/quicksy/server/throttle/VolumeAttempt.java @@ -46,6 +46,4 @@ public class VolumeAttempt<E, T> { public List<T> getWhats() { return whats; } - - } diff --git a/src/main/java/im/quicksy/server/throttle/VolumeLimiter.java b/src/main/java/im/quicksy/server/throttle/VolumeLimiter.java index a5beb7a..3aa2367 100644 --- a/src/main/java/im/quicksy/server/throttle/VolumeLimiter.java +++ b/src/main/java/im/quicksy/server/throttle/VolumeLimiter.java @@ -26,7 +26,7 @@ public class VolumeLimiter<E, T> { private final Strategy strategy; - private final LinkedList<VolumeAttempt<E,T>> attempts = new LinkedList<>(); + private final LinkedList<VolumeAttempt<E, T>> attempts = new LinkedList<>(); public VolumeLimiter(Strategy strategy) { this.strategy = strategy; @@ -39,18 +39,19 @@ public class VolumeLimiter<E, T> { } if (whats.size() > strategy.getAttempts()) { - throw new RetryInException(Duration.ZERO); //TODO should we throw a too large exception. because retry doesn’t really make sense + throw new RetryInException( + Duration.ZERO); // TODO should we throw a too large exception. because retry + // doesn’t really make sense } List<T> remaining = new ArrayList<>(whats); final long now = System.nanoTime(); - - final Iterator<VolumeAttempt<E,T>> iterator = attempts.iterator(); + final Iterator<VolumeAttempt<E, T>> iterator = attempts.iterator(); int whatsCount = 0; - VolumeAttempt<E,T> firstAttempt = null; + VolumeAttempt<E, T> firstAttempt = null; while (iterator.hasNext()) { - VolumeAttempt<E,T> attempt = iterator.next(); + VolumeAttempt<E, T> attempt = iterator.next(); if (attempt.expired(now)) { iterator.remove(); } else { @@ -67,7 +68,8 @@ public class VolumeLimiter<E, T> { } } if (whatsCount + remaining.size() > strategy.getAttempts()) { - throw new RetryInException(firstAttempt == null ? Duration.ZERO : firstAttempt.expiresIn(now)); + throw new RetryInException( + firstAttempt == null ? Duration.ZERO : firstAttempt.expiresIn(now)); } attempts.push(new VolumeAttempt<>(who, remaining, strategy.getDuration().toNanos() + now)); } @@ -84,9 +86,7 @@ public class VolumeLimiter<E, T> { } public String getMessage() { - return "Retry in "+duration.toString(); + return "Retry in " + duration.toString(); } - } - } diff --git a/src/main/java/im/quicksy/server/utils/CimUtils.java b/src/main/java/im/quicksy/server/utils/CimUtils.java index 4dfe5c4..0a866a5 100644 --- a/src/main/java/im/quicksy/server/utils/CimUtils.java +++ b/src/main/java/im/quicksy/server/utils/CimUtils.java @@ -18,15 +18,14 @@ package im.quicksy.server.utils; import com.google.common.net.UrlEscapers; import im.quicksy.server.configuration.Configuration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import rocks.xmpp.addr.Jid; - import java.io.IOException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import rocks.xmpp.addr.Jid; public class CimUtils { @@ -38,22 +37,26 @@ public class CimUtils { return false; } try { - URL url = new URL("https://account.conversations.im/api/get-paying-account/"+ UrlEscapers.urlPathSegmentEscaper().escape(jid.toEscapedString())+"/"); + URL url = + new URL( + "https://account.conversations.im/api/get-paying-account/" + + UrlEscapers.urlPathSegmentEscaper() + .escape(jid.toEscapedString()) + + "/"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestProperty("Authorization", cimAuthToken.get()); final int code = connection.getResponseCode(); if (code == 200) { - LOGGER.info(jid.toEscapedString()+" is a paying account"); + LOGGER.info(jid.toEscapedString() + " is a paying account"); return true; } else { - LOGGER.info(jid.toEscapedString()+" is not a paying account. code was="+code); + LOGGER.info(jid.toEscapedString() + " is not a paying account. code was=" + code); } } catch (MalformedURLException e) { - LOGGER.error("unable to create lookup url "+e.getMessage()); + LOGGER.error("unable to create lookup url " + e.getMessage()); } catch (IOException e) { LOGGER.error("unable to lookup paying account"); } return false; } - } diff --git a/src/main/java/im/quicksy/server/utils/CodeGenerator.java b/src/main/java/im/quicksy/server/utils/CodeGenerator.java index b150fcf..b0b2714 100644 --- a/src/main/java/im/quicksy/server/utils/CodeGenerator.java +++ b/src/main/java/im/quicksy/server/utils/CodeGenerator.java @@ -22,12 +22,11 @@ public class CodeGenerator { private static final char[] VALID_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789".toCharArray(); - public static String generate(int length) { final SecureRandom random = new SecureRandom(); StringBuilder builder = new StringBuilder(); - for(int i = 0; i < length; ++i) { - builder.append(VALID_CHARS[random.nextInt(VALID_CHARS.length -1)]); + for (int i = 0; i < length; ++i) { + builder.append(VALID_CHARS[random.nextInt(VALID_CHARS.length - 1)]); } return builder.toString(); } diff --git a/src/main/java/im/quicksy/server/utils/PayPal.java b/src/main/java/im/quicksy/server/utils/PayPal.java index e24d10d..15c9f48 100644 --- a/src/main/java/im/quicksy/server/utils/PayPal.java +++ b/src/main/java/im/quicksy/server/utils/PayPal.java @@ -19,8 +19,6 @@ package im.quicksy.server.utils; import im.quicksy.server.configuration.Configuration; import im.quicksy.server.pojo.Payment; import im.quicksy.server.pojo.ShoppingCartItem; - -import javax.net.ssl.HttpsURLConnection; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.InputStreamReader; @@ -30,6 +28,7 @@ import java.net.URLEncoder; import java.text.DecimalFormat; import java.util.HashMap; import java.util.Map; +import javax.net.ssl.HttpsURLConnection; public class PayPal { @@ -39,29 +38,35 @@ public class PayPal { private static final String ENCODING = "UTF-8"; - private static final String REDIRECTION_URL = "https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token="; + private static final String REDIRECTION_URL = + "https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token="; private static DecimalFormat CURRENCY_FORMAT = new DecimalFormat("0.00"); private static final String CALLBACK_URL_BASE = "https://quicksy.im/enter/paypal/"; public static String setExpressCheckout(Payment payment) throws PayPalAPIException { - HashMap<String,String> params = new HashMap<>(); - params.put("PAYMENTREQUEST_0_AMT",CURRENCY_FORMAT.format(payment.getTotal())); //CURRENCY_FORMAT.format(amount)); - params.put("PAYMENTREQUEST_0_ITEMAMT",CURRENCY_FORMAT.format(payment.getTotal())); + HashMap<String, String> params = new HashMap<>(); + params.put( + "PAYMENTREQUEST_0_AMT", + CURRENCY_FORMAT.format(payment.getTotal())); // CURRENCY_FORMAT.format(amount)); + params.put("PAYMENTREQUEST_0_ITEMAMT", CURRENCY_FORMAT.format(payment.getTotal())); int i = 0; - for(ShoppingCartItem item : payment.getItems()) { - params.put("L_PAYMENTREQUEST_0_NAME"+Integer.toString(i), item.getDescription()); - params.put("L_PAYMENTREQUEST_0_QTY"+Integer.toString(i), "1"); - params.put("L_PAYMENTREQUEST_0_AMT0"+Integer.toString(i), CURRENCY_FORMAT.format(item.getPrice())); + for (ShoppingCartItem item : payment.getItems()) { + params.put("L_PAYMENTREQUEST_0_NAME" + Integer.toString(i), item.getDescription()); + params.put("L_PAYMENTREQUEST_0_QTY" + Integer.toString(i), "1"); + params.put( + "L_PAYMENTREQUEST_0_AMT0" + Integer.toString(i), + CURRENCY_FORMAT.format(item.getPrice())); ++i; } - params.put("PAYMENTREQUEST_0_CURRENCYCODE","EUR"); - params.put("PAYMENTREQUEST_0_PAYMENTACTION","SALE"); - params.put("NOSHIPPING","1"); - params.put("BRANDNAME","Quicksy"); - params.put("cancelUrl",CALLBACK_URL_BASE+"cancel/"+payment.getUuid().toString()+"/"); - params.put("returnUrl",CALLBACK_URL_BASE+"success/"+payment.getUuid().toString()+"/"); + params.put("PAYMENTREQUEST_0_CURRENCYCODE", "EUR"); + params.put("PAYMENTREQUEST_0_PAYMENTACTION", "SALE"); + params.put("NOSHIPPING", "1"); + params.put("BRANDNAME", "Quicksy"); + params.put("cancelUrl", CALLBACK_URL_BASE + "cancel/" + payment.getUuid().toString() + "/"); + params.put( + "returnUrl", CALLBACK_URL_BASE + "success/" + payment.getUuid().toString() + "/"); try { HashMap<String, String> result = executeApiCall("SetExpressCheckout", params); System.out.println(result); @@ -71,14 +76,15 @@ public class PayPal { throw new PayPalAPIException("call was unsuccessful"); } return token; - } catch(Exception e) { + } catch (Exception e) { throw new PayPalAPIException(e.getMessage()); } } - public static HashMap<String,String> getExpressCheckoutDetails(String token) throws PayPalAPIException { - HashMap<String,String> params = new HashMap<>(); - params.put("TOKEN",token); + public static HashMap<String, String> getExpressCheckoutDetails(String token) + throws PayPalAPIException { + HashMap<String, String> params = new HashMap<>(); + params.put("TOKEN", token); try { return executeApiCall("GetExpressCheckoutDetails", params); } catch (Exception e) { @@ -86,33 +92,36 @@ public class PayPal { } } - public static boolean doExpressCheckoutPayment(String token, String payerId, double total) throws PayPalAPIException { - HashMap<String,String> params = new HashMap<>(); - params.put("TOKEN",token); - params.put("PAYERID",payerId); - params.put("PAYMENTREQUEST_0_AMT",CURRENCY_FORMAT.format(total)); - params.put("PAYMENTREQUEST_0_CURRENCYCODE","EUR"); - params.put("PAYMENTREQUEST_0_PAYMENTACTION","SALE"); + public static boolean doExpressCheckoutPayment(String token, String payerId, double total) + throws PayPalAPIException { + HashMap<String, String> params = new HashMap<>(); + params.put("TOKEN", token); + params.put("PAYERID", payerId); + params.put("PAYMENTREQUEST_0_AMT", CURRENCY_FORMAT.format(total)); + params.put("PAYMENTREQUEST_0_CURRENCYCODE", "EUR"); + params.put("PAYMENTREQUEST_0_PAYMENTACTION", "SALE"); try { - HashMap<String,String> result = executeApiCall("DoExpressCheckoutPayment", params); - System.out.println("result from doExpressCheckoutPayment"+result); + HashMap<String, String> result = executeApiCall("DoExpressCheckoutPayment", params); + System.out.println("result from doExpressCheckoutPayment" + result); return "Success".equals(result.get("ACK")); } catch (Exception e) { throw new PayPalAPIException(e.getMessage()); } } - private static HashMap<String,String> executeApiCall(String method, HashMap<String,String> params) throws Exception { + private static HashMap<String, String> executeApiCall( + String method, HashMap<String, String> params) throws Exception { Configuration.PayPal config = Configuration.getInstance().getPayPal(); - params.put("METHOD",method); - params.put("USER",config.getUsername()); + params.put("METHOD", method); + params.put("USER", config.getUsername()); params.put("PWD", config.getPassword()); params.put("SIGNATURE", config.getSignature()); - params.put("VERSION",String.valueOf(VERSION)); + params.put("VERSION", String.valueOf(VERSION)); return executeApiCall(params); } - private static HashMap<String,String> executeApiCall(HashMap<String,String> params) throws Exception { + private static HashMap<String, String> executeApiCall(HashMap<String, String> params) + throws Exception { final URL url = new URL(API_URL); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setRequestMethod("POST"); @@ -120,32 +129,34 @@ public class PayPal { connection.setDoOutput(true); DataOutputStream writer = new DataOutputStream(connection.getOutputStream()); StringBuilder postData = new StringBuilder(); - for(Map.Entry<String,String> param : params.entrySet()) { + for (Map.Entry<String, String> param : params.entrySet()) { if (postData.length() != 0) { postData.append('&'); } - postData.append(URLEncoder.encode(param.getKey(),ENCODING)); + postData.append(URLEncoder.encode(param.getKey(), ENCODING)); postData.append("="); - postData.append(URLEncoder.encode(param.getValue(),ENCODING)); + postData.append(URLEncoder.encode(param.getValue(), ENCODING)); } - System.out.println("post data "+postData.toString()); + System.out.println("post data " + postData.toString()); byte[] postDataBytes = postData.toString().getBytes(ENCODING); writer.write(postDataBytes); - HashMap<String,String> result = new HashMap<>(); - BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + HashMap<String, String> result = new HashMap<>(); + BufferedReader reader = + new BufferedReader(new InputStreamReader(connection.getInputStream())); StringBuilder builder = new StringBuilder(); - for (int c; (c = reader.read()) >= 0;) { + for (int c; (c = reader.read()) >= 0; ) { builder.append((char) c); } - for(String pair : builder.toString().split("&")) { - String[] parts = pair.split("=",2); - result.put(URLDecoder.decode(parts[0],ENCODING),URLDecoder.decode(parts[1],ENCODING)); + for (String pair : builder.toString().split("&")) { + String[] parts = pair.split("=", 2); + result.put( + URLDecoder.decode(parts[0], ENCODING), URLDecoder.decode(parts[1], ENCODING)); } return result; } public static String getRedirectionUrl(String token) { - return REDIRECTION_URL+token; + return REDIRECTION_URL + token; } private static class PayPalAPIException extends Exception { @@ -154,5 +165,4 @@ public class PayPal { super(message); } } - } diff --git a/src/main/java/im/quicksy/server/verification/AbstractVerificationProvider.java b/src/main/java/im/quicksy/server/verification/AbstractVerificationProvider.java index 0f82e9f..561c80c 100644 --- a/src/main/java/im/quicksy/server/verification/AbstractVerificationProvider.java +++ b/src/main/java/im/quicksy/server/verification/AbstractVerificationProvider.java @@ -4,8 +4,5 @@ import java.util.Map; public abstract class AbstractVerificationProvider implements VerificationProvider { - public AbstractVerificationProvider(final Map<String, String> parameter) { - - } - + public AbstractVerificationProvider(final Map<String, String> parameter) {} } diff --git a/src/main/java/im/quicksy/server/verification/FixedPinVerificationProvider.java b/src/main/java/im/quicksy/server/verification/FixedPinVerificationProvider.java index fb55ccb..d1b0d49 100644 --- a/src/main/java/im/quicksy/server/verification/FixedPinVerificationProvider.java +++ b/src/main/java/im/quicksy/server/verification/FixedPinVerificationProvider.java @@ -5,11 +5,10 @@ import com.google.common.base.Strings; import com.google.common.hash.Hashing; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.Phonenumber; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.math.BigInteger; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class FixedPinVerificationProvider extends AbstractVerificationProvider { diff --git a/src/main/java/im/quicksy/server/verification/MetaVerificationProvider.java b/src/main/java/im/quicksy/server/verification/MetaVerificationProvider.java index 81e4867..260b5ef 100644 --- a/src/main/java/im/quicksy/server/verification/MetaVerificationProvider.java +++ b/src/main/java/im/quicksy/server/verification/MetaVerificationProvider.java @@ -5,15 +5,14 @@ import com.google.common.collect.ImmutableList; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.Phonenumber; import im.quicksy.server.configuration.Configuration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.regex.Pattern; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class MetaVerificationProvider implements VerificationProvider { @@ -22,9 +21,11 @@ public class MetaVerificationProvider implements VerificationProvider { final List<ProviderWrapper> providerList; public MetaVerificationProvider() { - final TreeMap<String, Configuration.ProviderConfiguration> provider = Configuration.getInstance().getProvider(); + final TreeMap<String, Configuration.ProviderConfiguration> provider = + Configuration.getInstance().getProvider(); ImmutableList.Builder<ProviderWrapper> providerListBuilder = ImmutableList.builder(); - for(final Map.Entry<String,Configuration.ProviderConfiguration> entry : provider.entrySet()) { + for (final Map.Entry<String, Configuration.ProviderConfiguration> entry : + provider.entrySet()) { final String className = entry.getKey(); final Configuration.ProviderConfiguration configuration = entry.getValue(); final Class<? extends AbstractVerificationProvider> clazz; @@ -36,16 +37,22 @@ public class MetaVerificationProvider implements VerificationProvider { } final AbstractVerificationProvider providerInstance; try { - Constructor<? extends AbstractVerificationProvider> constructor = clazz.getConstructor(Map.class); + Constructor<? extends AbstractVerificationProvider> constructor = + clazz.getConstructor(Map.class); providerInstance = constructor.newInstance(configuration.getParameter()); } catch (NoSuchMethodException e) { - LOGGER.warn("{} does not implement Map<String,String> constructor", clazz.getName()); + LOGGER.warn( + "{} does not implement Map<String,String> constructor", clazz.getName()); continue; - } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { - LOGGER.warn("Unable to construct VerificationProvider",e); + } catch (IllegalAccessException + | InstantiationException + | InvocationTargetException e) { + LOGGER.warn("Unable to construct VerificationProvider", e); continue; } - providerListBuilder.add(new ProviderWrapper(configuration.getDeny(), configuration.getPattern(), providerInstance)); + providerListBuilder.add( + new ProviderWrapper( + configuration.getDeny(), configuration.getPattern(), providerInstance)); LOGGER.info("found provider {} ", className); } final ImmutableList<ProviderWrapper> providerList = providerListBuilder.build(); @@ -57,30 +64,37 @@ public class MetaVerificationProvider implements VerificationProvider { } @Override - public boolean verify(Phonenumber.PhoneNumber phoneNumber, String pin) throws RequestFailedException { + public boolean verify(Phonenumber.PhoneNumber phoneNumber, String pin) + throws RequestFailedException { return getVerificationProvider(phoneNumber).verify(phoneNumber, pin); } @Override - public void request(Phonenumber.PhoneNumber phoneNumber, Method method) throws RequestFailedException { + public void request(Phonenumber.PhoneNumber phoneNumber, Method method) + throws RequestFailedException { getVerificationProvider(phoneNumber).request(phoneNumber, method); } @Override - public void request(Phonenumber.PhoneNumber phoneNumber, Method method, String language) throws RequestFailedException { + public void request(Phonenumber.PhoneNumber phoneNumber, Method method, String language) + throws RequestFailedException { getVerificationProvider(phoneNumber).request(phoneNumber, method, language); } - private AbstractVerificationProvider getVerificationProvider(Phonenumber.PhoneNumber phoneNumber) throws RequestFailedException { + private AbstractVerificationProvider getVerificationProvider( + Phonenumber.PhoneNumber phoneNumber) throws RequestFailedException { final int countryCode = phoneNumber.getCountryCode(); - final String e164 = PhoneNumberUtil.getInstance().format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164); - for(final ProviderWrapper providerWrapper : this.providerList) { + final String e164 = + PhoneNumberUtil.getInstance() + .format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164); + for (final ProviderWrapper providerWrapper : this.providerList) { if (providerWrapper.reject(e164) || providerWrapper.deny.contains(countryCode)) { continue; } return providerWrapper.provider; } - throw new RequestFailedException(String.format("No Verification Provider found to handle phone number %s", e164)); + throw new RequestFailedException( + String.format("No Verification Provider found to handle phone number %s", e164)); } private static class ProviderWrapper { @@ -88,7 +102,8 @@ public class MetaVerificationProvider implements VerificationProvider { private final Pattern pattern; private final AbstractVerificationProvider provider; - private ProviderWrapper(List<Integer> deny, Pattern pattern, AbstractVerificationProvider provider) { + private ProviderWrapper( + List<Integer> deny, Pattern pattern, AbstractVerificationProvider provider) { this.deny = Preconditions.checkNotNull(deny); this.pattern = pattern; this.provider = Preconditions.checkNotNull(provider); diff --git a/src/main/java/im/quicksy/server/verification/MockVerificationProvider.java b/src/main/java/im/quicksy/server/verification/MockVerificationProvider.java index c6c3e91..e44e401 100644 --- a/src/main/java/im/quicksy/server/verification/MockVerificationProvider.java +++ b/src/main/java/im/quicksy/server/verification/MockVerificationProvider.java @@ -17,11 +17,10 @@ package im.quicksy.server.verification; import com.google.i18n.phonenumbers.Phonenumber; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Map; - public class MockVerificationProvider extends AbstractVerificationProvider { private static final Logger LOGGER = LoggerFactory.getLogger(MockVerificationProvider.class); @@ -32,7 +31,9 @@ public class MockVerificationProvider extends AbstractVerificationProvider { @Override public boolean verify(Phonenumber.PhoneNumber phoneNumber, String pin) { - return pin != null && pin.length() == 6 && String.valueOf(phoneNumber.getNationalNumber()).startsWith(pin); + return pin != null + && pin.length() == 6 + && String.valueOf(phoneNumber.getNationalNumber()).startsWith(pin); } @Override @@ -42,6 +43,12 @@ public class MockVerificationProvider extends AbstractVerificationProvider { @Override public void request(Phonenumber.PhoneNumber phoneNumber, Method method, String language) { - LOGGER.info("sending verification SMS to "+phoneNumber+"("+method+") language="+language); + LOGGER.info( + "sending verification SMS to " + + phoneNumber + + "(" + + method + + ") language=" + + language); } } diff --git a/src/main/java/im/quicksy/server/verification/NexmoVerificationProvider.java b/src/main/java/im/quicksy/server/verification/NexmoVerificationProvider.java index a4bcba9..5c35b8e 100644 --- a/src/main/java/im/quicksy/server/verification/NexmoVerificationProvider.java +++ b/src/main/java/im/quicksy/server/verification/NexmoVerificationProvider.java @@ -8,43 +8,41 @@ import com.google.common.math.IntMath; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.i18n.phonenumbers.Phonenumber; -import im.quicksy.server.configuration.Configuration; import im.quicksy.server.verification.nexmo.GenericResponse; -import okhttp3.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.security.SecureRandom; import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.Map; +import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class NexmoVerificationProvider extends AbstractVerificationProvider { private static final Logger LOGGER = LoggerFactory.getLogger(NexmoVerificationProvider.class); - private static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient.Builder() - //.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) - .build(); + private static final OkHttpClient OK_HTTP_CLIENT = + new OkHttpClient.Builder() + // .addInterceptor(new + // HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) + .build(); private static final Gson GSON = new GsonBuilder().create(); private static final HttpUrl NEXMO_API_URL = HttpUrl.get("https://rest.nexmo.com/sms/json"); private static final String BRAND_NAME = "Quicksy.im"; - private static final String MESSAGE = "Your Quicksy code is: %s\n\nDon't share this code with others.\n\nOYITl6r6eIp"; + private static final String MESSAGE = + "Your Quicksy code is: %s\n\nDon't share this code with others.\n\nOYITl6r6eIp"; - private static final List<Integer> COUNTRY_CODES_SUPPORTING_ALPHA_NUMERIC = Arrays.asList( - 49 - ); + private static final List<Integer> COUNTRY_CODES_SUPPORTING_ALPHA_NUMERIC = Arrays.asList(49); private static final int MAX_ATTEMPTS = 3; private static final SecureRandom SECURE_RANDOM = new SecureRandom(); - private final Cache<Phonenumber.PhoneNumber, Pin> PIN_CACHE = CacheBuilder.newBuilder() - .expireAfterWrite(Duration.ofMinutes(5)) - .build(); + private final Cache<Phonenumber.PhoneNumber, Pin> PIN_CACHE = + CacheBuilder.newBuilder().expireAfterWrite(Duration.ofMinutes(5)).build(); private final String phoneNumber; private final String apiKey; @@ -58,7 +56,8 @@ public class NexmoVerificationProvider extends AbstractVerificationProvider { } @Override - public boolean verify(Phonenumber.PhoneNumber phoneNumber, String input) throws RequestFailedException { + public boolean verify(Phonenumber.PhoneNumber phoneNumber, String input) + throws RequestFailedException { final Pin pin = PIN_CACHE.getIfPresent(phoneNumber); if (pin == null) { LOGGER.warn("The pin for {} has expired", phoneNumber); @@ -79,28 +78,35 @@ public class NexmoVerificationProvider extends AbstractVerificationProvider { } @Override - public void request(Phonenumber.PhoneNumber phoneNumber, Method method) throws RequestFailedException { + public void request(Phonenumber.PhoneNumber phoneNumber, Method method) + throws RequestFailedException { final Pin pin = Pin.generate(); PIN_CACHE.put(phoneNumber, pin); - final String to = String.format("%d%d", phoneNumber.getCountryCode(), phoneNumber.getNationalNumber()); + final String to = + String.format( + "%d%d", phoneNumber.getCountryCode(), phoneNumber.getNationalNumber()); final String nexmoPhoneNumber = this.phoneNumber; final String from; - if (Strings.isNullOrEmpty(nexmoPhoneNumber) || COUNTRY_CODES_SUPPORTING_ALPHA_NUMERIC.contains(phoneNumber.getCountryCode())) { + if (Strings.isNullOrEmpty(nexmoPhoneNumber) + || COUNTRY_CODES_SUPPORTING_ALPHA_NUMERIC.contains(phoneNumber.getCountryCode())) { from = BRAND_NAME; } else { from = nexmoPhoneNumber; } LOGGER.info("requesting SMS through nexmo for {}", phoneNumber); - final Call call = OK_HTTP_CLIENT.newCall(new Request.Builder() - .post(new FormBody.Builder() - .add("from", from) - .add("text", String.format(MESSAGE, pin.toString())) - .add("to", to) - .add("api_key", this.apiKey) - .add("api_secret", this.apiSecret) - .build()) - .url(NEXMO_API_URL) - .build()); + final Call call = + OK_HTTP_CLIENT.newCall( + new Request.Builder() + .post( + new FormBody.Builder() + .add("from", from) + .add("text", String.format(MESSAGE, pin.toString())) + .add("to", to) + .add("api_key", this.apiKey) + .add("api_secret", this.apiSecret) + .build()) + .url(NEXMO_API_URL) + .build()); try { final Response response = call.execute(); final int code = response.code(); @@ -112,13 +118,17 @@ public class NexmoVerificationProvider extends AbstractVerificationProvider { if (body == null) { throw new RequestFailedException("Empty body"); } - final GenericResponse nexmoResponse = GSON.fromJson(body.charStream(), GenericResponse.class); + final GenericResponse nexmoResponse = + GSON.fromJson(body.charStream(), GenericResponse.class); final List<GenericResponse.Message> messages = nexmoResponse.getMessages(); if (messages.size() >= 1) { final GenericResponse.Message message = messages.get(0); final String status = message.getStatus(); if (!"0".equals(status)) { - LOGGER.error("Unable to requests SMS. Status={} text={}", message.getStatus(), message.getErrorText()); + LOGGER.error( + "Unable to requests SMS. Status={} text={}", + message.getStatus(), + message.getErrorText()); throw new RequestFailedException(message.getErrorText()); } } else { @@ -132,7 +142,8 @@ public class NexmoVerificationProvider extends AbstractVerificationProvider { } @Override - public void request(Phonenumber.PhoneNumber phoneNumber, Method method, String language) throws RequestFailedException { + public void request(Phonenumber.PhoneNumber phoneNumber, Method method, String language) + throws RequestFailedException { request(phoneNumber, method); } @@ -145,12 +156,14 @@ public class NexmoVerificationProvider extends AbstractVerificationProvider { } public static Pin generate() { - final int pin = SECURE_RANDOM.nextInt(IntMath.pow(10, VerificationProvider.VERIFICATION_CODE_LENGTH)); - return new Pin(Strings.padStart( - String.valueOf(pin), - VerificationProvider.VERIFICATION_CODE_LENGTH, - '0' - )); + final int pin = + SECURE_RANDOM.nextInt( + IntMath.pow(10, VerificationProvider.VERIFICATION_CODE_LENGTH)); + return new Pin( + Strings.padStart( + String.valueOf(pin), + VerificationProvider.VERIFICATION_CODE_LENGTH, + '0')); } public synchronized boolean verify(String pin) { @@ -167,7 +180,5 @@ public class NexmoVerificationProvider extends AbstractVerificationProvider { } } - public static class TooManyAttemptsException extends RuntimeException { - - } + public static class TooManyAttemptsException extends RuntimeException {} } diff --git a/src/main/java/im/quicksy/server/verification/RequestFailedException.java b/src/main/java/im/quicksy/server/verification/RequestFailedException.java index dc1808f..5ce8f3a 100644 --- a/src/main/java/im/quicksy/server/verification/RequestFailedException.java +++ b/src/main/java/im/quicksy/server/verification/RequestFailedException.java @@ -20,7 +20,7 @@ public class RequestFailedException extends Exception { private int code; public RequestFailedException(String message, Throwable throwable) { - super(message,throwable); + super(message, throwable); } public RequestFailedException(Throwable throwable) { diff --git a/src/main/java/im/quicksy/server/verification/TokenExpiredException.java b/src/main/java/im/quicksy/server/verification/TokenExpiredException.java index af29a5f..5bafb1a 100644 --- a/src/main/java/im/quicksy/server/verification/TokenExpiredException.java +++ b/src/main/java/im/quicksy/server/verification/TokenExpiredException.java @@ -6,7 +6,7 @@ public class TokenExpiredException extends RequestFailedException { } public TokenExpiredException(String message) { - super(message,0); + super(message, 0); } public TokenExpiredException(Exception e) { diff --git a/src/main/java/im/quicksy/server/verification/TwilioVerificationProvider.java b/src/main/java/im/quicksy/server/verification/TwilioVerificationProvider.java index f366e0d..583b22e 100644 --- a/src/main/java/im/quicksy/server/verification/TwilioVerificationProvider.java +++ b/src/main/java/im/quicksy/server/verification/TwilioVerificationProvider.java @@ -25,9 +25,6 @@ import im.quicksy.server.configuration.Configuration; import im.quicksy.server.verification.twilio.ErrorResponse; import im.quicksy.server.verification.twilio.GenericResponse; import im.quicksy.server.verification.twilio.StartResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStreamWriter; @@ -37,16 +34,18 @@ import java.net.URL; import java.net.URLEncoder; import java.util.*; import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class TwilioVerificationProvider extends AbstractVerificationProvider { - public static final int PHONE_VERIFICATION_INCORRECT = 60022; public static final int PHONE_VERIFICATION_NOT_FOUND = 60023; public static final int PHONE_NUMBER_IS_INVALID = 60033; public static final int PHONE_NUMBER_IS_NOT_A_VALID_MOBILE_NUMBER = 21614; - private static final String TWILIO_API_URL = "https://api.authy.com/protected/json/phones/verification/"; + private static final String TWILIO_API_URL = + "https://api.authy.com/protected/json/phones/verification/"; private static final Logger LOGGER = LoggerFactory.getLogger(TwilioVerificationProvider.class); private final GsonBuilder gsonBuilder = new GsonBuilder(); @@ -59,16 +58,20 @@ public class TwilioVerificationProvider extends AbstractVerificationProvider { public TwilioVerificationProvider() { super(Collections.emptyMap()); - final TreeMap<String, Configuration.ProviderConfiguration> provider = Configuration.getInstance().getProvider(); - final Configuration.ProviderConfiguration myConfiguration = provider.get(getClass().getName()); + final TreeMap<String, Configuration.ProviderConfiguration> provider = + Configuration.getInstance().getProvider(); + final Configuration.ProviderConfiguration myConfiguration = + provider.get(getClass().getName()); if (myConfiguration == null) { - throw new RuntimeException("No configuration found for "+getClass().getSimpleName()); + throw new RuntimeException("No configuration found for " + getClass().getSimpleName()); } - this.authToken = Preconditions.checkNotNull(myConfiguration.getParameter().get("auth_token")); + this.authToken = + Preconditions.checkNotNull(myConfiguration.getParameter().get("auth_token")); } @Override - public boolean verify(Phonenumber.PhoneNumber phoneNumber, String pin) throws RequestFailedException { + public boolean verify(Phonenumber.PhoneNumber phoneNumber, String pin) + throws RequestFailedException { Map<String, String> params = new HashMap<>(); params.put("phone_number", Long.toString(phoneNumber.getNationalNumber())); params.put("country_code", Integer.toString(phoneNumber.getCountryCode())); @@ -93,13 +96,15 @@ public class TwilioVerificationProvider extends AbstractVerificationProvider { } @Override - public void request(Phonenumber.PhoneNumber phoneNumber, Method method) throws RequestFailedException { + public void request(Phonenumber.PhoneNumber phoneNumber, Method method) + throws RequestFailedException { request(phoneNumber, method, null); } @Override - public void request(Phonenumber.PhoneNumber phoneNumber, Method method, String language) throws RequestFailedException { - LOGGER.info("requesting verification ("+method.toString()+") for " + phoneNumber); + public void request(Phonenumber.PhoneNumber phoneNumber, Method method, String language) + throws RequestFailedException { + LOGGER.info("requesting verification (" + method.toString() + ") for " + phoneNumber); Map<String, String> params = new HashMap<>(); params.put("via", method.toString().toLowerCase(Locale.ENGLISH)); params.put("phone_number", Long.toString(phoneNumber.getNationalNumber())); @@ -135,23 +140,31 @@ public class TwilioVerificationProvider extends AbstractVerificationProvider { return result.toString(); } - private <T> T execute(final String method, final Map<String, String> params, Class<T> clazz) throws RequestFailedException { + private <T> T execute(final String method, final Map<String, String> params, Class<T> clazz) + throws RequestFailedException { String result = null; try { final Gson gson = this.gsonBuilder.create(); - final HttpURLConnection connection = (HttpURLConnection) new URL(TWILIO_API_URL + method).openConnection(); + final HttpURLConnection connection = + (HttpURLConnection) new URL(TWILIO_API_URL + method).openConnection(); connection.setRequestProperty("X-Authy-API-Key", this.authToken); if (params != null && params.size() > 0) { connection.setRequestMethod("POST"); final String output = getQuery(params); connection.setDoOutput(true); - OutputStreamWriter outputStream = new OutputStreamWriter(connection.getOutputStream()); + OutputStreamWriter outputStream = + new OutputStreamWriter(connection.getOutputStream()); outputStream.write(output); outputStream.flush(); outputStream.close(); } int code = connection.getResponseCode(); - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(code == 200 ? connection.getInputStream() : connection.getErrorStream())); + BufferedReader bufferedReader = + new BufferedReader( + new InputStreamReader( + code == 200 + ? connection.getInputStream() + : connection.getErrorStream())); result = bufferedReader.lines().collect(Collectors.joining("\n")); if (code == 200) { return gson.fromJson(result, clazz); @@ -166,7 +179,10 @@ public class TwilioVerificationProvider extends AbstractVerificationProvider { } } catch (JsonSyntaxException e) { final String firstLine = result == null ? "" : result.split("\n")[0]; - throw new RequestFailedException("Unable to parse JSON starting with " + firstLine.substring(0, Math.min(firstLine.length(), 20)), e); + throw new RequestFailedException( + "Unable to parse JSON starting with " + + firstLine.substring(0, Math.min(firstLine.length(), 20)), + e); } catch (RequestFailedException e) { throw e; } catch (Throwable t) { diff --git a/src/main/java/im/quicksy/server/verification/VerificationProvider.java b/src/main/java/im/quicksy/server/verification/VerificationProvider.java index 1b06f77..2b9f877 100644 --- a/src/main/java/im/quicksy/server/verification/VerificationProvider.java +++ b/src/main/java/im/quicksy/server/verification/VerificationProvider.java @@ -26,11 +26,11 @@ public interface VerificationProvider { void request(Phonenumber.PhoneNumber phoneNumber, Method method) throws RequestFailedException; - void request(Phonenumber.PhoneNumber phoneNumber, Method method, String language) throws RequestFailedException; + void request(Phonenumber.PhoneNumber phoneNumber, Method method, String language) + throws RequestFailedException; enum Method { SMS, CALL } - } diff --git a/src/main/java/im/quicksy/server/verification/nexmo/ErrorResponse.java b/src/main/java/im/quicksy/server/verification/nexmo/ErrorResponse.java index 3a47be4..23aee45 100644 --- a/src/main/java/im/quicksy/server/verification/nexmo/ErrorResponse.java +++ b/src/main/java/im/quicksy/server/verification/nexmo/ErrorResponse.java @@ -5,5 +5,4 @@ public class ErrorResponse { private String type; private String title; private String detail; - } diff --git a/src/main/java/im/quicksy/server/verification/nexmo/GenericResponse.java b/src/main/java/im/quicksy/server/verification/nexmo/GenericResponse.java index df0f235..f50f414 100644 --- a/src/main/java/im/quicksy/server/verification/nexmo/GenericResponse.java +++ b/src/main/java/im/quicksy/server/verification/nexmo/GenericResponse.java @@ -1,7 +1,6 @@ package im.quicksy.server.verification.nexmo; import com.google.gson.annotations.SerializedName; - import java.util.List; public class GenericResponse { @@ -22,6 +21,7 @@ public class GenericResponse { public static class Message { private String to; private String status; + @SerializedName("error-text") private String errorText; diff --git a/src/main/java/im/quicksy/server/verification/twilio/ErrorResponse.java b/src/main/java/im/quicksy/server/verification/twilio/ErrorResponse.java index 8115ccf..4ca892a 100644 --- a/src/main/java/im/quicksy/server/verification/twilio/ErrorResponse.java +++ b/src/main/java/im/quicksy/server/verification/twilio/ErrorResponse.java @@ -26,5 +26,4 @@ public class ErrorResponse extends GenericResponse { public int getErrorCode() { return errorCode; } - } diff --git a/src/main/java/im/quicksy/server/verification/twilio/StartResponse.java b/src/main/java/im/quicksy/server/verification/twilio/StartResponse.java index 1d9f1a6..72c5611 100644 --- a/src/main/java/im/quicksy/server/verification/twilio/StartResponse.java +++ b/src/main/java/im/quicksy/server/verification/twilio/StartResponse.java @@ -38,5 +38,4 @@ public class StartResponse extends GenericResponse { public String getUuid() { return uuid; } - } diff --git a/src/main/java/im/quicksy/server/xmpp/synchronization/Entry.java b/src/main/java/im/quicksy/server/xmpp/synchronization/Entry.java index 17a771e..59c3b9a 100644 --- a/src/main/java/im/quicksy/server/xmpp/synchronization/Entry.java +++ b/src/main/java/im/quicksy/server/xmpp/synchronization/Entry.java @@ -17,29 +17,25 @@ package im.quicksy.server.xmpp.synchronization; import com.google.common.io.BaseEncoding; -import rocks.xmpp.addr.Jid; - -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import rocks.xmpp.addr.Jid; @XmlRootElement public class Entry implements Comparable<Entry> { - @XmlAttribute - private String number; + @XmlAttribute private String number; - @XmlElement(name="jid", namespace = PhoneBook.NAMESPACE) + @XmlElement(name = "jid", namespace = PhoneBook.NAMESPACE) private List<Jid> jids; - private Entry() { - - } + private Entry() {} public Entry(String number) { this.number = number; @@ -62,14 +58,14 @@ public class Entry implements Comparable<Entry> { public static String statusQuo(final List<Entry> entries) { Collections.sort(entries); StringBuilder builder = new StringBuilder(); - for(Entry entry : entries) { + for (Entry entry : entries) { if (builder.length() != 0) { builder.append('\u001d'); } builder.append(entry.getNumber()); List<Jid> jids = entry.getJids(); Collections.sort(jids); - for(Jid jid : jids) { + for (Jid jid : jids) { builder.append('\u001e'); builder.append(jid.asBareJid().toEscapedString()); } diff --git a/src/main/java/im/quicksy/server/xmpp/synchronization/PhoneBook.java b/src/main/java/im/quicksy/server/xmpp/synchronization/PhoneBook.java index 1a1bc34..eb34163 100644 --- a/src/main/java/im/quicksy/server/xmpp/synchronization/PhoneBook.java +++ b/src/main/java/im/quicksy/server/xmpp/synchronization/PhoneBook.java @@ -17,33 +17,34 @@ package im.quicksy.server.xmpp.synchronization; import im.quicksy.server.controller.BaseController; - -import javax.xml.bind.annotation.*; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import javax.xml.bind.annotation.*; @XmlRootElement(name = "phone-book", namespace = PhoneBook.NAMESPACE) public class PhoneBook { public static final String NAMESPACE = "im.quicksy.synchronization:0"; - @XmlAttribute - private String ver; + @XmlAttribute private String ver; - @XmlElement(name="entry") + @XmlElement(name = "entry") private List<Entry> entries; - private PhoneBook() { - - } + private PhoneBook() {} public PhoneBook(List<Entry> entries) { this.entries = entries; } public List<String> getPhoneNumbers() { - return entries == null ? Collections.emptyList() : entries.stream().map(Entry::getNumber).filter(n -> BaseController.E164_PATTERN.matcher(n).matches()).collect(Collectors.toList()); + return entries == null + ? Collections.emptyList() + : entries.stream() + .map(Entry::getNumber) + .filter(n -> BaseController.E164_PATTERN.matcher(n).matches()) + .collect(Collectors.toList()); } public String getVer() { diff --git a/src/main/java/im/quicksy/server/xmpp/synchronization/package-info.java b/src/main/java/im/quicksy/server/xmpp/synchronization/package-info.java index 6687f53..94ec2d5 100644 --- a/src/main/java/im/quicksy/server/xmpp/synchronization/package-info.java +++ b/src/main/java/im/quicksy/server/xmpp/synchronization/package-info.java @@ -21,4 +21,4 @@ package im.quicksy.server.xmpp.synchronization; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlNsForm; -import javax.xml.bind.annotation.XmlSchema; \ No newline at end of file +import javax.xml.bind.annotation.XmlSchema; diff --git a/src/test/java/im/quicksy/server/DatabaseTest.java b/src/test/java/im/quicksy/server/DatabaseTest.java index 95cbacc..65cd49e 100644 --- a/src/test/java/im/quicksy/server/DatabaseTest.java +++ b/src/test/java/im/quicksy/server/DatabaseTest.java @@ -16,6 +16,8 @@ package im.quicksy.server; +import static junit.framework.TestCase.*; + import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; import im.quicksy.server.configuration.DatabaseConfiguration; @@ -24,14 +26,10 @@ import im.quicksy.server.database.Database; import im.quicksy.server.pojo.Entry; import im.quicksy.server.pojo.Payment; import im.quicksy.server.pojo.PaymentMethod; +import java.util.Collections; import org.junit.Test; import rocks.xmpp.addr.Jid; -import java.util.Collections; -import java.util.List; - -import static junit.framework.TestCase.*; - public class DatabaseTest { private static final String JDBC_URL = "jdbc:sqlite::memory:"; @@ -43,17 +41,15 @@ public class DatabaseTest { private static final DatabaseConfigurationBundle IN_MEMORY_DATABASE_CONFIGURATION; static { - IN_MEMORY_DATABASE_CONFIGURATION = new DatabaseConfigurationBundle.Builder() - .setEjabberdConfiguration(new DatabaseConfiguration.Builder() - .setUrl(JDBC_URL) - .build()) - .setQuicksyConfiguration(new DatabaseConfiguration.Builder() - .setUrl(JDBC_URL) - .build()) - .build(); + IN_MEMORY_DATABASE_CONFIGURATION = + new DatabaseConfigurationBundle.Builder() + .setEjabberdConfiguration( + new DatabaseConfiguration.Builder().setUrl(JDBC_URL).build()) + .setQuicksyConfiguration( + new DatabaseConfiguration.Builder().setUrl(JDBC_URL).build()) + .build(); } - @Test public void makePaymentCreateEntityAndReadBack() { final Database database = new Database(IN_MEMORY_DATABASE_CONFIGURATION); @@ -76,10 +72,13 @@ public class DatabaseTest { final Entry entry = new Entry(TEST_USER); entry.setPhoneNumber(PhoneNumberUtil.getInstance().parse(TEST_PHONE_NUMBER, "us")); assertTrue(database.updatePaymentAndCreateEntry(payment, entry)); - assertEquals(0, database.findDirectoryUsers(Collections.singletonList(TEST_PHONE_NUMBER)).size()); + assertEquals( + 0, + database.findDirectoryUsers(Collections.singletonList(TEST_PHONE_NUMBER)).size()); entry.setVerified(true); database.updateEntry(entry); - assertEquals(1, database.findDirectoryUsers(Collections.singletonList(TEST_PHONE_NUMBER)).size()); + assertEquals( + 1, + database.findDirectoryUsers(Collections.singletonList(TEST_PHONE_NUMBER)).size()); } - } diff --git a/src/test/java/im/quicksy/server/NexmoVerificationProviderTest.java b/src/test/java/im/quicksy/server/NexmoVerificationProviderTest.java index d258f44..34c9407 100644 --- a/src/test/java/im/quicksy/server/NexmoVerificationProviderTest.java +++ b/src/test/java/im/quicksy/server/NexmoVerificationProviderTest.java @@ -7,8 +7,7 @@ import org.junit.rules.ExpectedException; public class NexmoVerificationProviderTest { - @Rule - public final ExpectedException expectedException = ExpectedException.none(); + @Rule public final ExpectedException expectedException = ExpectedException.none(); @Test public void pinExpiry() { diff --git a/src/test/java/im/quicksy/server/VolumeRateLimiterTest.java b/src/test/java/im/quicksy/server/VolumeRateLimiterTest.java index 11058a7..3c048a3 100644 --- a/src/test/java/im/quicksy/server/VolumeRateLimiterTest.java +++ b/src/test/java/im/quicksy/server/VolumeRateLimiterTest.java @@ -2,75 +2,70 @@ package im.quicksy.server; import im.quicksy.server.throttle.Strategy; import im.quicksy.server.throttle.VolumeLimiter; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import rocks.xmpp.addr.Jid; - import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import rocks.xmpp.addr.Jid; public class VolumeRateLimiterTest { - @Rule - public final ExpectedException expectedException = ExpectedException.none(); + @Rule public final ExpectedException expectedException = ExpectedException.none(); @Test public void hittingMultipleTimesWithSameWhat() throws VolumeLimiter.RetryInException { - VolumeLimiter<Jid, String> volumeLimiter = new VolumeLimiter<>( - Strategy.of(Duration.ofHours(1),5) - ); + VolumeLimiter<Jid, String> volumeLimiter = + new VolumeLimiter<>(Strategy.of(Duration.ofHours(1), 5)); Jid personA = Jid.of("person@a.com"); Jid personB = Jid.of("person@b.com"); - volumeLimiter.attempt(personA, Arrays.asList("1","2","3")); - volumeLimiter.attempt(personA, Arrays.asList("3","4","5")); - volumeLimiter.attempt(personA, Arrays.asList("1","2")); + volumeLimiter.attempt(personA, Arrays.asList("1", "2", "3")); + volumeLimiter.attempt(personA, Arrays.asList("3", "4", "5")); + volumeLimiter.attempt(personA, Arrays.asList("1", "2")); - volumeLimiter.attempt(personB, Arrays.asList("9","8","7")); - volumeLimiter.attempt(personB, Arrays.asList("7","6","9")); - volumeLimiter.attempt(personB, Arrays.asList("9","6")); + volumeLimiter.attempt(personB, Arrays.asList("9", "8", "7")); + volumeLimiter.attempt(personB, Arrays.asList("7", "6", "9")); + volumeLimiter.attempt(personB, Arrays.asList("9", "6")); } @Test public void hittingOnceTooLarge() throws VolumeLimiter.RetryInException { - VolumeLimiter<Jid, String> volumeLimiter = new VolumeLimiter<>( - Strategy.of(Duration.ofHours(1),5) - ); + VolumeLimiter<Jid, String> volumeLimiter = + new VolumeLimiter<>(Strategy.of(Duration.ofHours(1), 5)); expectedException.expect(VolumeLimiter.RetryInException.class); - volumeLimiter.attempt(Jid.of("person@domain.tld"),Arrays.asList("1","2","3","4","5","6")); + volumeLimiter.attempt( + Jid.of("person@domain.tld"), Arrays.asList("1", "2", "3", "4", "5", "6")); } @Test public void gettingTooLargeOverTime() throws VolumeLimiter.RetryInException { - VolumeLimiter<Jid, String> volumeLimiter = new VolumeLimiter<>( - Strategy.of(Duration.ofHours(1),5) - ); + VolumeLimiter<Jid, String> volumeLimiter = + new VolumeLimiter<>(Strategy.of(Duration.ofHours(1), 5)); Jid person = Jid.of("person@a.com"); - volumeLimiter.attempt(person,Arrays.asList("1","2","3","4")); + volumeLimiter.attempt(person, Arrays.asList("1", "2", "3", "4")); volumeLimiter.attempt(person, Collections.singletonList("5")); expectedException.expect(VolumeLimiter.RetryInException.class); - volumeLimiter.attempt(person, Arrays.asList("6","7")); + volumeLimiter.attempt(person, Arrays.asList("6", "7")); } @Test public void expiry() throws VolumeLimiter.RetryInException, InterruptedException { - VolumeLimiter<Jid, String> volumeLimiter = new VolumeLimiter<>( - Strategy.of(Duration.ofSeconds(5), 5) - ); + VolumeLimiter<Jid, String> volumeLimiter = + new VolumeLimiter<>(Strategy.of(Duration.ofSeconds(5), 5)); Jid personA = Jid.of("person@a.com"); Jid personB = Jid.of("person@b.com"); - volumeLimiter.attempt(personA, Arrays.asList("1","2","3","4","5")); + volumeLimiter.attempt(personA, Arrays.asList("1", "2", "3", "4", "5")); Thread.sleep(1000); - volumeLimiter.attempt(personB, Arrays.asList("a","b","c","d")); + volumeLimiter.attempt(personB, Arrays.asList("a", "b", "c", "d")); Thread.sleep(5000); - volumeLimiter.attempt(personA, Arrays.asList("6","7","8","9")); + volumeLimiter.attempt(personA, Arrays.asList("6", "7", "8", "9")); Thread.sleep(1000); - volumeLimiter.attempt(personB, Arrays.asList("e","f","g","h","i")); + volumeLimiter.attempt(personB, Arrays.asList("e", "f", "g", "h", "i")); } @Test @@ -84,13 +79,15 @@ public class VolumeRateLimiterTest { rolling(1001); } - private void rolling(long timeBetweenAttempts) throws InterruptedException, VolumeLimiter.RetryInException { - List<Jid> persons = Arrays.asList(Jid.of("a@example.com"),Jid.of("b@example.com"),Jid.of("c@example.com")); - VolumeLimiter<Jid, String> volumeLimiter = new VolumeLimiter<>( - Strategy.of(Duration.ofSeconds(5), 5) - ); - for(int i = 0; i <= 10; ++i) { - for(Jid jid : persons) { + private void rolling(long timeBetweenAttempts) + throws InterruptedException, VolumeLimiter.RetryInException { + List<Jid> persons = + Arrays.asList( + Jid.of("a@example.com"), Jid.of("b@example.com"), Jid.of("c@example.com")); + VolumeLimiter<Jid, String> volumeLimiter = + new VolumeLimiter<>(Strategy.of(Duration.ofSeconds(5), 5)); + for (int i = 0; i <= 10; ++i) { + for (Jid jid : persons) { volumeLimiter.attempt(jid, Collections.singletonList(String.valueOf(i))); } if (i != 10) { @@ -98,5 +95,4 @@ public class VolumeRateLimiterTest { } } } - } -- GitLab