Adding mongo support and /antivpn clearcache command

This commit is contained in:
Dawson Hessler
2022-01-12 15:37:02 -05:00
parent 7ee04b74ea
commit cd502b6f34
22 changed files with 375 additions and 63 deletions
@@ -6,6 +6,7 @@ import dev.brighten.antivpn.api.VPNExecutor;
import dev.brighten.antivpn.command.Command;
import dev.brighten.antivpn.command.impl.AntiVPNCommand;
import dev.brighten.antivpn.database.VPNDatabase;
import dev.brighten.antivpn.database.mongo.MongoVPN;
import dev.brighten.antivpn.database.sql.MySqlVPN;
import dev.brighten.antivpn.message.MessageHandler;
import dev.brighten.antivpn.utils.VPNResponse;
@@ -61,7 +62,8 @@ public class AntiVPN {
case "mongo":
case "mongodb":
case "mongod": {
AntiVPN.getInstance().getExecutor().log("We currently do not support Mongo, but this is coming in future updates.");
INSTANCE.database = new MongoVPN();
INSTANCE.database.init();
break;
}
default: {
@@ -24,6 +24,10 @@ public interface VPNConfig {
boolean isDatabaseEnabled();
boolean useDatabaseCreds();
String mongoDatabaseURL();
String getDatabaseType();
String getDatabaseName();
@@ -15,7 +15,7 @@ import java.util.function.Consumer;
public abstract class VPNExecutor {
public static ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
private static final Map<String, VPNResponse> responseCache = new HashMap<>();
public static final Map<String, VPNResponse> responseCache = new HashMap<>();
@Getter
private final Set<UUID> whitelisted = Collections.synchronizedSet(new HashSet<>());
@Getter
@@ -41,7 +41,8 @@ public class AntiVPNCommand extends Command {
@Override
public Command[] children() {
return new Command[] {new LookupCommand(), new AllowlistCommand(), new AlertsCommand()};
return new Command[] {new LookupCommand(), new AllowlistCommand(), new AlertsCommand(),
new ClearCacheCommand()};
}
@Override
@@ -0,0 +1,58 @@
package dev.brighten.antivpn.command.impl;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.VPNExecutor;
import dev.brighten.antivpn.command.Command;
import dev.brighten.antivpn.command.CommandExecutor;
import java.util.Collections;
import java.util.List;
public class ClearCacheCommand extends Command {
@Override
public String permission() {
return "antivpn.command.clearcache";
}
@Override
public String name() {
return "clearcache";
}
@Override
public String[] aliases() {
return new String[] {"clear", "cc"};
}
@Override
public String description() {
return "Clear the API response cache if you're having problems.";
}
@Override
public String usage() {
return "";
}
@Override
public String parent() {
return "antivpn";
}
@Override
public Command[] children() {
return new Command[0];
}
@Override
public String execute(CommandExecutor executor, String[] args) {
AntiVPN.getInstance().getDatabase().clearResponses();
VPNExecutor.responseCache.clear();
return "&aCleared all cached API response information!";
}
@Override
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
return Collections.emptyList();
}
}
@@ -34,6 +34,8 @@ public interface VPNDatabase {
void updateAlertsState(UUID uuid, boolean state);
void clearResponses();
void init();
void shutdown();
@@ -0,0 +1,208 @@
package dev.brighten.antivpn.database.mongo;
import com.mongodb.*;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Indexes;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.VPNExecutor;
import dev.brighten.antivpn.database.VPNDatabase;
import dev.brighten.antivpn.utils.VPNResponse;
import org.bson.Document;
import java.util.*;
import java.util.function.Consumer;
public class MongoVPN implements VPNDatabase {
private MongoCollection<Document> settingsDocument, cacheDocument;
private MongoClient client;
private MongoDatabase antivpnDatabase;
@Override
public Optional<VPNResponse> getStoredResponse(String ip) {
Document rdoc = cacheDocument.find(Filters.eq("ip", ip)).first();
if(rdoc != null) {
return Optional.of(VPNResponse.builder().asn(rdoc.getString("asn")).ip(ip)
.countryName(rdoc.getString("countryName"))
.countryCode(rdoc.getString("countryCode"))
.city(rdoc.getString("city"))
.isp(rdoc.getString("isp"))
.method(rdoc.getString("method"))
.timeZone(rdoc.getString("timeZone"))
.proxy(rdoc.getBoolean("proxy"))
.cached(rdoc.getBoolean("cached"))
.success(true)
.latitude(rdoc.getDouble("latitude"))
.longitude(rdoc.getDouble("longitude"))
.build());
}
return Optional.empty();
}
@Override
public void cacheResponse(VPNResponse toCache) {
Document rdoc = new Document("ip", toCache.getIp());
rdoc.put("asn", toCache.getAsn());
rdoc.put("countryName", toCache.getCountryName());
rdoc.put("countryCode", toCache.getCountryCode());
rdoc.put("city", toCache.getCity());
rdoc.put("isp", toCache.getIsp());
rdoc.put("method", toCache.getMethod());
rdoc.put("timeZone", toCache.getTimeZone());
rdoc.put("proxy", toCache.isProxy());
rdoc.put("cached", toCache.isCached());
rdoc.put("success", toCache.isSuccess());
rdoc.put("latitude", toCache.getLatitude());
rdoc.put("longitude", toCache.getLongitude());
VPNExecutor.threadExecutor.execute(() -> {
cacheDocument.deleteMany(Filters.eq("ip", toCache.getIp()));
cacheDocument.insertOne(rdoc);
});
}
@Override
public boolean isWhitelisted(UUID uuid) {
return settingsDocument
.find(Filters.and(Filters.eq("setting", "whitelist"),
Filters.eq("uuid", uuid.toString()))).first() != null;
}
@Override
public boolean isWhitelisted(String ip) {
return settingsDocument
.find(Filters.and(Filters.eq("setting", "whitelist"),
Filters.eq("ip", ip))).first() != null;
}
@Override
public void setWhitelisted(UUID uuid, boolean whitelisted) {
if(whitelisted) {
Document wdoc = new Document("setting", "whitelist");
wdoc.put("uuid", uuid.toString());
VPNExecutor.threadExecutor.execute(() -> settingsDocument.insertOne(wdoc));
} else {
VPNExecutor.threadExecutor.execute(() -> settingsDocument.deleteMany(Filters
.and(
Filters.eq("setting", "whitelist"),
Filters.eq("uuid", uuid.toString()))));
}
}
@Override
public void setWhitelisted(String ip, boolean whitelisted) {
if(whitelisted) {
Document wdoc = new Document("setting", "whitelist");
wdoc.put("ip", ip);
VPNExecutor.threadExecutor.execute(() -> settingsDocument.insertOne(wdoc));
} else {
VPNExecutor.threadExecutor.execute(() -> settingsDocument.deleteMany(Filters
.and(
Filters.eq("setting", "whitelist"),
Filters.eq("ip", ip))));
}
}
@Override
public List<UUID> getAllWhitelisted() {
List<UUID> uuids = new ArrayList<>();
settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
Filters.exists("uuid")))
.forEach((Consumer<? super Document>) doc -> uuids.add(UUID.fromString(doc.getString("uuid"))));
return uuids;
}
@Override
public List<String> getAllWhitelistedIps() {
List<String> ips = new ArrayList<>();
settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
Filters.exists("ip")))
.forEach((Consumer<? super Document>) doc -> ips.add(doc.getString("ip")));
return ips;
}
@Override
public void getStoredResponseAsync(String ip, Consumer<Optional<VPNResponse>> result) {
VPNExecutor.threadExecutor.execute(() -> result.accept(getStoredResponse(ip)));
}
@Override
public void isWhitelistedAsync(UUID uuid, Consumer<Boolean> result) {
VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(uuid)));
}
@Override
public void isWhitelistedAsync(String ip, Consumer<Boolean> result) {
VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(ip)));
}
@Override
public void alertsState(UUID uuid, Consumer<Boolean> result) {
VPNExecutor.threadExecutor.execute(() -> result.accept(settingsDocument
.find(Filters.and(Filters.eq("setting", "alerts"),
Filters.eq("uuid", uuid.toString()))).first() != null));
}
@Override
public void updateAlertsState(UUID uuid, boolean state) {
VPNExecutor.threadExecutor.execute(() -> {
settingsDocument.deleteMany(Filters.and(Filters.eq("setting", "alerts"),
Filters.eq("uuid", uuid.toString())));
if(state) {
Document adoc = new Document("setting", "alerts");
adoc.put("uuid", uuid.toString());
settingsDocument.insertOne(adoc);
}
});
}
@Override
public void clearResponses() {
cacheDocument.deleteMany(Filters.exists("ip"));
}
@Override
public void init() {
if(AntiVPN.getInstance().getConfig().mongoDatabaseURL().length() > 0) { //URL
ConnectionString cs = new ConnectionString(AntiVPN.getInstance().getConfig().mongoDatabaseURL());
MongoClientSettings settings = MongoClientSettings.builder().applyConnectionString(cs).build();
client = MongoClients.create(settings);
} else {
MongoClientSettings.Builder settingsBld = MongoClientSettings.builder().readPreference(ReadPreference.nearest())
.applyToClusterSettings(builder -> {
builder.hosts(Collections.singletonList(new ServerAddress(AntiVPN.getInstance().getConfig().getIp(),
AntiVPN.getInstance().getConfig().getPort())));
});
if(AntiVPN.getInstance().getConfig().useDatabaseCreds()) {
settingsBld = settingsBld.credential(MongoCredential
.createCredential(AntiVPN.getInstance().getConfig().getUsername(),
AntiVPN.getInstance().getConfig().getDatabaseName(),
AntiVPN.getInstance().getConfig().getPassword().toCharArray()));
}
client = MongoClients.create(settingsBld.build());
}
antivpnDatabase = client.getDatabase(AntiVPN.getInstance().getConfig().getDatabaseName());
settingsDocument = antivpnDatabase.getCollection("settings");
if(settingsDocument.listIndexes().first() == null) {
AntiVPN.getInstance().getExecutor().log("Created index for settings collection!");
settingsDocument.createIndex(Indexes.ascending("ip"));
}
cacheDocument = antivpnDatabase.getCollection("cache");
}
@Override
public void shutdown() {
settingsDocument = null;
cacheDocument = null;
client.close();
}
}
@@ -225,6 +225,13 @@ public class MySqlVPN implements VPNDatabase {
.execute());
}
@Override
public void clearResponses() {
if(MySQL.isClosed()) return;
VPNExecutor.threadExecutor.execute(() -> Query.prepare("delete from `responses`").execute());
}
@Override
public void init() {
if (!AntiVPN.getInstance().getConfig().isDatabaseEnabled())
@@ -41,33 +41,4 @@ public class MiscUtils {
return false;
}
}
/* Borrowed from FireFlyx ngxdev */
public static void download(File file, String from) throws Exception {
URL url = new URL(from);
InputStream stream = url.openStream();
ReadableByteChannel channel = Channels.newChannel(stream);
FileOutputStream out = new FileOutputStream(file);
out.getChannel().transferFrom(channel, 0L, Long.MAX_VALUE);
}
/* Borrowed from FireFlyx ngxdev */
public static ClassLoader injectorClassLoader = MiscUtils.class.getClassLoader();
/* Borrowed from FireFlyx ngxdev */
public static void injectURL(URL url) {
try {
URLClassLoader systemClassLoader = (URLClassLoader) injectorClassLoader;
Class<URLClassLoader> classLoaderClass = URLClassLoader.class;
try {
Method method = classLoaderClass.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(systemClassLoader, url);
} catch (Throwable t) {
t.printStackTrace();
}
} catch (Exception e) {
}
}
}
@@ -2,15 +2,13 @@ package dev.brighten.antivpn.utils;
import dev.brighten.antivpn.utils.json.JSONException;
import dev.brighten.antivpn.utils.json.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.*;
@Getter
@Setter
@AllArgsConstructor
@RequiredArgsConstructor
@Builder
public class VPNResponse {
private String asn, ip, countryName, countryCode, city, timeZone, method, isp, failureReason = "N/A";
private boolean proxy, cached;