remove the bad patch system
This commit is contained in:
+129
@@ -0,0 +1,129 @@
|
||||
<!--
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2021 <Your name and contributors>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>net.limework.forks.LimeLogin</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<artifactId>limelogin-parent</artifactId>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>limelogin.core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>LimeLoginCore</name>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>luck-repo</id>
|
||||
<url>https://ci.lucko.me/plugin/repository/everything</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>codemc-repo</id>
|
||||
<url>https://repo.codemc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
<!-- Floodgate -->
|
||||
<repository>
|
||||
<id>opencollab-snapshot</id>
|
||||
<url>https://repo.opencollab.dev/maven-snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<!-- Libraries that we shade into the project -->
|
||||
|
||||
<!--Database pooling-->
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>4.0.3</version>
|
||||
</dependency>
|
||||
|
||||
<!--Logging framework implements slf4j which is required by hikari-->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<version>1.7.32</version>
|
||||
</dependency>
|
||||
|
||||
<!-- snakeyaml is present in Bungee, Spigot, Cauldron and so we could use this independent implementation -->
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-config</artifactId>
|
||||
<version>1.16-R0.5-SNAPSHOT</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-proxy</artifactId>
|
||||
<version>1.16-R0.5-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!--Floodgate for Xbox Live Authentication-->
|
||||
<dependency>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!--Common component for contacting the Mojang API-->
|
||||
<dependency>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>craftapi</artifactId>
|
||||
<version>0.4</version>
|
||||
</dependency>
|
||||
|
||||
<!-- APIs we can use because they are available in all platforms (Spigot, Bungee) -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>17.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.2.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
* This limits the number of threads that are used at maximum. Thread creation can be very heavy for the CPU and
|
||||
* context switching between threads too. However we need many threads for blocking HTTP and database calls.
|
||||
* Nevertheless this number can be further limited, because the number of actually working database threads
|
||||
* is limited by the size of our database pool. The goal is to separate concerns into processing and blocking only
|
||||
* threads.
|
||||
*/
|
||||
public class AsyncScheduler {
|
||||
|
||||
private static final int MAX_CAPACITY = 1024;
|
||||
|
||||
//todo: single thread for delaying and scheduling tasks
|
||||
private final Logger logger;
|
||||
|
||||
// 30 threads are still too many - the optimal solution is to separate into processing and blocking threads
|
||||
// where processing threads could only be max number of cores while blocking threads could be minimized using
|
||||
// non-blocking I/O and a single event executor
|
||||
private final ExecutorService processingPool;
|
||||
|
||||
/*
|
||||
private final ExecutorService databaseExecutor = new ThreadPoolExecutor(1, 10,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<>(MAX_CAPACITY));
|
||||
*/
|
||||
|
||||
public AsyncScheduler(Logger logger, ThreadFactory threadFactory) {
|
||||
this.logger = logger;
|
||||
processingPool = new ThreadPoolExecutor(6, 32,
|
||||
60L, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(MAX_CAPACITY), threadFactory);
|
||||
}
|
||||
|
||||
/*
|
||||
public <R> CompletableFuture<R> runDatabaseTask(Supplier<R> databaseTask) {
|
||||
return CompletableFuture.supplyAsync(databaseTask, databaseExecutor)
|
||||
.exceptionally(error -> {
|
||||
logger.warn("Error occurred on thread pool", error);
|
||||
return null;
|
||||
})
|
||||
// change context to the processing pool
|
||||
.thenApplyAsync(r -> r, processingPool);
|
||||
}
|
||||
*/
|
||||
|
||||
public CompletableFuture<Void> runAsync(Runnable task) {
|
||||
return CompletableFuture.runAsync(task, processingPool).exceptionally(error -> {
|
||||
logger.warn("Error occurred on thread pool", error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
MoreExecutors.shutdownAndAwaitTermination(processingPool, 1, TimeUnit.MINUTES);
|
||||
//MoreExecutors.shutdownAndAwaitTermination(databaseExecutor, 1, TimeUnit.MINUTES);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import com.github.games647.craftapi.cache.SafeCacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.impl.JDK14LoggerAdapter;
|
||||
|
||||
public class CommonUtil {
|
||||
|
||||
private static final char COLOR_CHAR = '&';
|
||||
private static final char TRANSLATED_CHAR = '§';
|
||||
|
||||
public static <K, V> ConcurrentMap<K, V> buildCache(int expireAfterWrite, int maxSize) {
|
||||
SafeCacheBuilder<Object, Object> builder = SafeCacheBuilder.newBuilder();
|
||||
|
||||
if (expireAfterWrite > 0) {
|
||||
builder.expireAfterWrite(expireAfterWrite, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
if (maxSize > 0) {
|
||||
builder.maximumSize(maxSize);
|
||||
}
|
||||
|
||||
return builder.build(CacheLoader.from(() -> {
|
||||
throw new UnsupportedOperationException();
|
||||
}));
|
||||
}
|
||||
|
||||
public static String translateColorCodes(String rawMessage) {
|
||||
char[] chars = rawMessage.toCharArray();
|
||||
for (int i = 0; i < chars.length - 1; i++) {
|
||||
if (chars[i] == COLOR_CHAR && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(chars[i + 1]) > -1) {
|
||||
chars[i] = TRANSLATED_CHAR;
|
||||
chars[i + 1] = Character.toLowerCase(chars[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
public static Logger createLoggerFromJDK(java.util.logging.Logger parent) {
|
||||
try {
|
||||
parent.setLevel(Level.ALL);
|
||||
|
||||
Class<JDK14LoggerAdapter> adapterClass = JDK14LoggerAdapter.class;
|
||||
Constructor<JDK14LoggerAdapter> cons = adapterClass.getDeclaredConstructor(java.util.logging.Logger.class);
|
||||
cons.setAccessible(true);
|
||||
return cons.newInstance(parent);
|
||||
} catch (ReflectiveOperationException reflectEx) {
|
||||
parent.log(Level.WARNING, "Cannot create slf4j logging adapter", reflectEx);
|
||||
parent.log(Level.WARNING, "Creating logger instance manually...");
|
||||
return LoggerFactory.getLogger(parent.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private CommonUtil() {
|
||||
//Utility class
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
public enum PremiumStatus {
|
||||
|
||||
PREMIUM("Premium"),
|
||||
|
||||
CRACKED("Cracked"),
|
||||
|
||||
UNKNOWN("Unknown");
|
||||
|
||||
private final String readableName;
|
||||
|
||||
PremiumStatus(String readableName) {
|
||||
this.readableName = readableName;
|
||||
}
|
||||
|
||||
public String getReadableName() {
|
||||
return readableName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
/**
|
||||
* Limit the number of requests with a maximum size. Each requests expires after the specified time making it available
|
||||
* for another request.
|
||||
*/
|
||||
public class RateLimiter {
|
||||
|
||||
private final long[] requests;
|
||||
private final long expireTime;
|
||||
private int position;
|
||||
|
||||
public RateLimiter(int maxLimit, long expireTime) {
|
||||
this.requests = new long[maxLimit];
|
||||
this.expireTime = expireTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask if access is allowed. If so register the request.
|
||||
*
|
||||
* @return true if allowed - false otherwise without any side effects
|
||||
*/
|
||||
public boolean tryAcquire() {
|
||||
// current time millis is not monotonic - it can jump back depending on user choice or NTP
|
||||
long now = System.nanoTime() / 1_000_000;
|
||||
|
||||
// after this the request should be expired
|
||||
long toBeExpired = now - expireTime;
|
||||
synchronized (this) {
|
||||
// having synchronized will limit the amount of concurrency a lot
|
||||
long oldest = requests[position];
|
||||
if (oldest < toBeExpired) {
|
||||
requests[position] = now;
|
||||
position = (position + 1) % requests.length;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import com.github.games647.craftapi.model.Profile;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class StoredProfile extends Profile {
|
||||
|
||||
private long rowId;
|
||||
private final ReentrantLock saveLock = new ReentrantLock();
|
||||
|
||||
private boolean premium;
|
||||
private String lastIp;
|
||||
private Instant lastLogin;
|
||||
|
||||
public StoredProfile(long rowId, UUID uuid, String playerName, boolean premium, String lastIp, Instant lastLogin) {
|
||||
super(uuid, playerName);
|
||||
|
||||
this.rowId = rowId;
|
||||
this.premium = premium;
|
||||
this.lastIp = lastIp;
|
||||
this.lastLogin = lastLogin;
|
||||
}
|
||||
|
||||
public StoredProfile(UUID uuid, String playerName, boolean premium, String lastIp) {
|
||||
this(-1, uuid, playerName, premium, lastIp, Instant.now());
|
||||
}
|
||||
|
||||
public ReentrantLock getSaveLock() {
|
||||
return saveLock;
|
||||
}
|
||||
|
||||
public synchronized boolean isSaved() {
|
||||
return rowId >= 0;
|
||||
}
|
||||
|
||||
public synchronized void setPlayerName(String playerName) {
|
||||
this.name = playerName;
|
||||
}
|
||||
|
||||
public synchronized long getRowId() {
|
||||
return rowId;
|
||||
}
|
||||
|
||||
public synchronized void setRowId(long generatedId) {
|
||||
this.rowId = generatedId;
|
||||
}
|
||||
|
||||
// can be null
|
||||
public synchronized UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public synchronized Optional<UUID> getOptId() {
|
||||
return Optional.ofNullable(id);
|
||||
}
|
||||
|
||||
public synchronized void setId(UUID uniqueId) {
|
||||
this.id = uniqueId;
|
||||
}
|
||||
|
||||
public synchronized boolean isPremium() {
|
||||
return premium;
|
||||
}
|
||||
|
||||
public synchronized void setPremium(boolean premium) {
|
||||
this.premium = premium;
|
||||
}
|
||||
|
||||
public synchronized String getLastIp() {
|
||||
return lastIp;
|
||||
}
|
||||
|
||||
public synchronized void setLastIp(String lastIp) {
|
||||
this.lastIp = lastIp;
|
||||
}
|
||||
|
||||
public synchronized Instant getLastLogin() {
|
||||
return lastLogin;
|
||||
}
|
||||
|
||||
public synchronized void setLastLogin(Instant lastLogin) {
|
||||
this.lastLogin = lastLogin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof StoredProfile)) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
StoredProfile that = (StoredProfile) o;
|
||||
return rowId == that.rowId && premium == that.premium
|
||||
&& Objects.equals(lastIp, that.lastIp) && lastLogin.equals(that.lastLogin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), rowId, premium, lastIp, lastLogin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return this.getClass().getSimpleName() + '{' +
|
||||
"rowId=" + rowId +
|
||||
", premium=" + premium +
|
||||
", lastIp='" + lastIp + '\'' +
|
||||
", lastLogin=" + lastLogin +
|
||||
"} " + super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.hooks;
|
||||
|
||||
/**
|
||||
* Represents a supporting authentication plugin in BungeeCord and Bukkit/Spigot/... servers
|
||||
*
|
||||
* @param <P> either {@link org.bukkit.entity.Player} for Bukkit or {@link net.md_5.bungee.api.connection.ProxiedPlayer}
|
||||
* for BungeeCord
|
||||
*/
|
||||
public interface AuthPlugin<P> {
|
||||
|
||||
String ALREADY_AUTHENTICATED = "Player {} is already authenticated. Cancelling force login.";
|
||||
|
||||
/**
|
||||
* Login the premium (paid account) player after the player joined successfully the server.
|
||||
*
|
||||
* <strong>This operation will be performed async while the player successfully
|
||||
* joined the server.</strong>
|
||||
*
|
||||
* @param player the player that needs to be logged in
|
||||
* @return if the operation was successful
|
||||
*/
|
||||
boolean forceLogin(P player);
|
||||
|
||||
/**
|
||||
* Forces a register in order to protect the paid account.
|
||||
*
|
||||
* <strong>This operation will be performed async while the player successfully
|
||||
* joined the server.</strong>
|
||||
*
|
||||
* After a successful registration the player should be logged
|
||||
* in too.
|
||||
*
|
||||
* The method will be called only for premium accounts.
|
||||
* So it's recommended to set additionally premium property
|
||||
* if possible.
|
||||
*
|
||||
* Background: If we don't register an account, cracked players
|
||||
* could steal the unregistered account from the paid
|
||||
* player account
|
||||
*
|
||||
* @param player the premium account
|
||||
* @param password a strong random generated password
|
||||
* @return if the operation was successful
|
||||
*/
|
||||
boolean forceRegister(P player, String password);
|
||||
|
||||
/**
|
||||
* Checks whether an account exists for this player name.
|
||||
*
|
||||
* This check should check if a cracked player account exists
|
||||
* so we can be sure the premium player doesn't steal the account
|
||||
* of that player.
|
||||
*
|
||||
* This operation will be performed async while the player is
|
||||
* connecting.
|
||||
*
|
||||
* @param playerName player name
|
||||
* @return if the player has an account
|
||||
* @throws Exception if an error occurred
|
||||
*/
|
||||
boolean isRegistered(String playerName) throws Exception;
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.hooks;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class DefaultPasswordGenerator<P> implements PasswordGenerator<P> {
|
||||
|
||||
private static final int PASSWORD_LENGTH = 8;
|
||||
private static final char[] PASSWORD_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
.toCharArray();
|
||||
|
||||
private final Random random = new SecureRandom();
|
||||
|
||||
@Override
|
||||
public String getRandomPassword(P player) {
|
||||
StringBuilder generatedPassword = new StringBuilder(8);
|
||||
IntStream.rangeClosed(1, PASSWORD_LENGTH)
|
||||
.map(i -> random.nextInt(PASSWORD_CHARACTERS.length - 1))
|
||||
.mapToObj(pos -> PASSWORD_CHARACTERS[pos])
|
||||
.forEach(generatedPassword::append);
|
||||
|
||||
return generatedPassword.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.hooks;
|
||||
|
||||
import com.github.games647.craftapi.model.Profile;
|
||||
import com.github.games647.craftapi.resolver.RateLimitException;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
public class FloodgateHook<P extends C, C, S extends LoginSource> {
|
||||
|
||||
private final FastLoginCore<P, C, ?> core;
|
||||
|
||||
public FloodgateHook(FastLoginCore<P, C, ?> core) {
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the player's name conflicts an existing Java player's name, and
|
||||
* kick them if it does
|
||||
*
|
||||
* @param username the name of the player
|
||||
* @param source an instance of LoginSource
|
||||
*/
|
||||
public void checkFloodgateNameConflict(String username, LoginSource source, FloodgatePlayer floodgatePlayer) {
|
||||
String allowConflict = core.getConfig().get("allowFloodgateNameConflict").toString().toLowerCase();
|
||||
|
||||
// check if the Bedrock player is linked to a Java account
|
||||
boolean isLinked = ((FloodgatePlayer) floodgatePlayer).getLinkedPlayer() != null;
|
||||
|
||||
if (allowConflict.equals("false")
|
||||
|| allowConflict.equals("linked") && !isLinked) {
|
||||
|
||||
// check for conflicting Premium Java name
|
||||
Optional<Profile> premiumUUID = Optional.empty();
|
||||
try {
|
||||
premiumUUID = core.getResolver().findProfile(username);
|
||||
} catch (IOException | RateLimitException e) {
|
||||
core.getPlugin().getLog().error(
|
||||
"Could not check wether Floodgate Player {}'s name conflicts a premium Java player's name.",
|
||||
username);
|
||||
try {
|
||||
source.kick("Could not check if your name conflicts an existing premium Java account's name.\n"
|
||||
+ "This is usually a serverside error.");
|
||||
} catch (Exception e1) {
|
||||
core.getPlugin().getLog().error("Could not kick Player {}", username);
|
||||
}
|
||||
}
|
||||
|
||||
if (premiumUUID.isPresent()) {
|
||||
core.getPlugin().getLog().info("Bedrock Player {}'s name conflicts an existing premium Java account's name",
|
||||
username);
|
||||
try {
|
||||
source.kick("Your name conflicts an existing premium Java account's name");
|
||||
} catch (Exception e) {
|
||||
core.getPlugin().getLog().error("Could not kick Player {}", username);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
core.getPlugin().getLog().info("Skipping name conflict checking for player {}", username);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The FloodgateApi does not support querying players by name, so this function
|
||||
* iterates over every online FloodgatePlayer and checks if the requested
|
||||
* username can be found
|
||||
*
|
||||
* @param username the name of the player
|
||||
* @return FloodgatePlayer if found, null otherwise
|
||||
*/
|
||||
public FloodgatePlayer getFloodgatePlayer(String username) {
|
||||
if (core.getPlugin().isPluginInstalled("floodgate")) {
|
||||
for (FloodgatePlayer floodgatePlayer : FloodgateApi.getInstance().getPlayers()) {
|
||||
if (floodgatePlayer.getUsername().equals(username)) {
|
||||
return floodgatePlayer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.hooks;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PasswordGenerator<P> {
|
||||
|
||||
String getRandomPassword(P player);
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.message;
|
||||
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
|
||||
public class ChangePremiumMessage implements ChannelMessage {
|
||||
|
||||
public static final String CHANGE_CHANNEL = "ch-st";
|
||||
|
||||
private String playerName;
|
||||
private boolean willEnable;
|
||||
private boolean isSourceInvoker;
|
||||
|
||||
public ChangePremiumMessage(String playerName, boolean willEnable, boolean isSourceInvoker) {
|
||||
this.playerName = playerName;
|
||||
this.willEnable = willEnable;
|
||||
this.isSourceInvoker = isSourceInvoker;
|
||||
}
|
||||
|
||||
public ChangePremiumMessage() {
|
||||
//reading from
|
||||
}
|
||||
|
||||
public String getPlayerName() {
|
||||
return playerName;
|
||||
}
|
||||
|
||||
public boolean shouldEnable() {
|
||||
return willEnable;
|
||||
}
|
||||
|
||||
public boolean isSourceInvoker() {
|
||||
return isSourceInvoker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelName() {
|
||||
return CHANGE_CHANNEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(ByteArrayDataInput input) {
|
||||
willEnable = input.readBoolean();
|
||||
playerName = input.readUTF();
|
||||
isSourceInvoker = input.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteArrayDataOutput output) {
|
||||
output.writeBoolean(willEnable);
|
||||
output.writeUTF(playerName);
|
||||
output.writeBoolean(isSourceInvoker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + '{' +
|
||||
"playerName='" + playerName + '\'' +
|
||||
", shouldEnable=" + willEnable +
|
||||
", isSourceInvoker=" + isSourceInvoker +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.message;
|
||||
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
|
||||
public interface ChannelMessage {
|
||||
|
||||
String getChannelName();
|
||||
|
||||
void readFrom(ByteArrayDataInput input);
|
||||
|
||||
void writeTo(ByteArrayDataOutput output);
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.message;
|
||||
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class LoginActionMessage implements ChannelMessage {
|
||||
|
||||
public static final String FORCE_CHANNEL = "force";
|
||||
|
||||
private Type type;
|
||||
|
||||
private String playerName;
|
||||
private UUID proxyId;
|
||||
|
||||
public LoginActionMessage(Type type, String playerName, UUID proxyId) {
|
||||
this.type = type;
|
||||
this.playerName = playerName;
|
||||
this.proxyId = proxyId;
|
||||
}
|
||||
|
||||
public LoginActionMessage() {
|
||||
//reading mode
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getPlayerName() {
|
||||
return playerName;
|
||||
}
|
||||
|
||||
public UUID getProxyId() {
|
||||
return proxyId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(ByteArrayDataInput input) {
|
||||
this.type = Type.values()[input.readInt()];
|
||||
|
||||
this.playerName = input.readUTF();
|
||||
|
||||
//bungeecord UUID
|
||||
long mostSignificantBits = input.readLong();
|
||||
long leastSignificantBits = input.readLong();
|
||||
this.proxyId = new UUID(mostSignificantBits, leastSignificantBits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteArrayDataOutput output) {
|
||||
output.writeInt(type.ordinal());
|
||||
|
||||
//Data is sent through a random player. We have to tell the Bukkit version of this plugin the target
|
||||
output.writeUTF(playerName);
|
||||
|
||||
//proxy identifier to check if it's a acceptable proxy
|
||||
output.writeLong(proxyId.getMostSignificantBits());
|
||||
output.writeLong(proxyId.getLeastSignificantBits());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelName() {
|
||||
return FORCE_CHANNEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + '{' +
|
||||
"type='" + type + '\'' +
|
||||
", playerName='" + playerName + '\'' +
|
||||
", proxyId=" + proxyId +
|
||||
'}';
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
|
||||
LOGIN,
|
||||
|
||||
REGISTER,
|
||||
|
||||
CRACKED
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.message;
|
||||
|
||||
public class NamespaceKey {
|
||||
|
||||
private static final char SEPARATOR_CHAR = ':';
|
||||
|
||||
private final String namespace;
|
||||
private final String key;
|
||||
|
||||
private final String combined;
|
||||
|
||||
public NamespaceKey(String namespace, String key) {
|
||||
this.namespace = namespace.toLowerCase();
|
||||
this.key = key.toLowerCase();
|
||||
|
||||
this.combined = this.namespace + SEPARATOR_CHAR + this.key;
|
||||
}
|
||||
|
||||
public String getCombinedName() {
|
||||
return combined;
|
||||
}
|
||||
|
||||
public static String getCombined(String namespace, String key) {
|
||||
return new NamespaceKey(namespace, key).combined;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.message;
|
||||
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
|
||||
public class SuccessMessage implements ChannelMessage {
|
||||
|
||||
public static final String SUCCESS_CHANNEL = "succ";
|
||||
|
||||
@Override
|
||||
public String getChannelName() {
|
||||
return SUCCESS_CHANNEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(ByteArrayDataInput input) {
|
||||
//empty
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(ByteArrayDataOutput output) {
|
||||
//empty
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + "{}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.craftapi.resolver.MojangResolver;
|
||||
import com.github.games647.craftapi.resolver.http.RotatingProxySelector;
|
||||
import com.github.games647.fastlogin.core.storage.MySQLStorage;
|
||||
import com.github.games647.fastlogin.core.storage.SQLStorage;
|
||||
import com.github.games647.fastlogin.core.CommonUtil;
|
||||
import com.github.games647.fastlogin.core.RateLimiter;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator;
|
||||
import com.github.games647.fastlogin.core.hooks.PasswordGenerator;
|
||||
import com.github.games647.fastlogin.core.storage.SQLiteStorage;
|
||||
import com.google.common.net.HostAndPort;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.Proxy.Type;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import net.md_5.bungee.config.ConfigurationProvider;
|
||||
import net.md_5.bungee.config.YamlConfiguration;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import static java.util.function.Function.identity;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
/**
|
||||
* @param <P> GameProfile class
|
||||
* @param <C> CommandSender
|
||||
* @param <T> Plugin class
|
||||
*/
|
||||
public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
|
||||
private static final long MAX_EXPIRE_RATE = 1_000_000;
|
||||
|
||||
private final Map<String, String> localeMessages = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<String, Object> pendingLogin = CommonUtil.buildCache(5, -1);
|
||||
private final Collection<UUID> pendingConfirms = new HashSet<>();
|
||||
private final T plugin;
|
||||
|
||||
private final MojangResolver resolver = new MojangResolver();
|
||||
|
||||
private Configuration config;
|
||||
private SQLStorage storage;
|
||||
private RateLimiter rateLimiter;
|
||||
private PasswordGenerator<P> passwordGenerator = new DefaultPasswordGenerator<>();
|
||||
private AuthPlugin<P> authPlugin;
|
||||
|
||||
public FastLoginCore(T plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void load() {
|
||||
saveDefaultFile("messages.yml");
|
||||
saveDefaultFile("config.yml");
|
||||
|
||||
try {
|
||||
config = loadFile("config.yml");
|
||||
Configuration messages = loadFile("messages.yml");
|
||||
|
||||
messages.getKeys()
|
||||
.stream()
|
||||
.filter(key -> messages.get(key) != null)
|
||||
.collect(toMap(identity(), messages::get))
|
||||
.forEach((key, message) -> {
|
||||
String colored = CommonUtil.translateColorCodes((String) message);
|
||||
if (!colored.isEmpty()) {
|
||||
localeMessages.put(key, colored.replace("/newline", "\n"));
|
||||
}
|
||||
});
|
||||
} catch (IOException ioEx) {
|
||||
plugin.getLog().error("Failed to load yaml files", ioEx);
|
||||
return;
|
||||
}
|
||||
|
||||
int maxCon = config.getInt("anti-bot.connections", 200);
|
||||
long expireTime = config.getLong("anti-bot.expire", 5) * 60 * 1_000L;
|
||||
if (expireTime > MAX_EXPIRE_RATE) {
|
||||
expireTime = MAX_EXPIRE_RATE;
|
||||
}
|
||||
|
||||
rateLimiter = new RateLimiter(maxCon, expireTime);
|
||||
Set<Proxy> proxies = config.getStringList("proxies")
|
||||
.stream()
|
||||
.map(HostAndPort::fromString)
|
||||
.map(proxy -> new InetSocketAddress(proxy.getHostText(), proxy.getPort()))
|
||||
.map(sa -> new Proxy(Type.HTTP, sa))
|
||||
.collect(toSet());
|
||||
|
||||
Collection<InetAddress> addresses = new HashSet<>();
|
||||
for (String localAddress : config.getStringList("ip-addresses")) {
|
||||
try {
|
||||
addresses.add(InetAddress.getByName(localAddress.replace('-', '.')));
|
||||
} catch (UnknownHostException ex) {
|
||||
plugin.getLog().error("IP-Address is unknown to us", ex);
|
||||
}
|
||||
}
|
||||
|
||||
resolver.setMaxNameRequests(config.getInt("mojang-request-limit"));
|
||||
resolver.setProxySelector(new RotatingProxySelector(proxies));
|
||||
resolver.setOutgoingAddresses(addresses);
|
||||
}
|
||||
|
||||
private Configuration loadFile(String fileName) throws IOException {
|
||||
ConfigurationProvider configProvider = ConfigurationProvider.getProvider(YamlConfiguration.class);
|
||||
|
||||
Configuration defaults;
|
||||
try (InputStream defaultStream = getClass().getClassLoader().getResourceAsStream(fileName)) {
|
||||
defaults = configProvider.load(defaultStream);
|
||||
}
|
||||
|
||||
Path file = plugin.getPluginFolder().resolve(fileName);
|
||||
|
||||
Configuration config;
|
||||
try (Reader reader = Files.newBufferedReader(file)) {
|
||||
config = configProvider.load(reader, defaults);
|
||||
}
|
||||
|
||||
// explicitly add keys here, because Configuration.getKeys doesn't return the keys from the default configuration
|
||||
for (String key : defaults.getKeys()) {
|
||||
config.set(key, config.get(key));
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public MojangResolver getResolver() {
|
||||
return resolver;
|
||||
}
|
||||
|
||||
public SQLStorage getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
public T getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public void sendLocaleMessage(String key, C receiver) {
|
||||
String message = localeMessages.get(key);
|
||||
if (message != null) {
|
||||
plugin.sendMultiLineMessage(receiver, message);
|
||||
}
|
||||
}
|
||||
|
||||
public String getMessage(String key) {
|
||||
return localeMessages.get(key);
|
||||
}
|
||||
|
||||
public boolean setupDatabase() {
|
||||
String driver = config.getString("driver");
|
||||
if (!checkDriver(driver)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HikariConfig databaseConfig = new HikariConfig();
|
||||
databaseConfig.setDriverClassName(driver);
|
||||
|
||||
String database = config.getString("database");
|
||||
|
||||
databaseConfig.setConnectionTimeout(config.getInt("timeout", 30) * 1_000L);
|
||||
databaseConfig.setMaxLifetime(config.getInt("lifetime", 30) * 1_000L);
|
||||
|
||||
if (driver.contains("sqlite")) {
|
||||
storage = new SQLiteStorage(this, database, databaseConfig);
|
||||
} else {
|
||||
String host = config.get("host", "");
|
||||
int port = config.get("port", 3306);
|
||||
boolean useSSL = config.get("useSSL", false);
|
||||
|
||||
if (useSSL) {
|
||||
databaseConfig.addDataSourceProperty("allowPublicKeyRetrieval", config.getBoolean("allowPublicKeyRetrieval", false));
|
||||
databaseConfig.addDataSourceProperty("serverRSAPublicKeyFile", config.getString("ServerRSAPublicKeyFile"));
|
||||
databaseConfig.addDataSourceProperty("sslMode", config.getString("sslMode", "Required"));
|
||||
}
|
||||
|
||||
databaseConfig.setUsername(config.get("username", ""));
|
||||
databaseConfig.setPassword(config.getString("password"));
|
||||
storage = new MySQLStorage(this, host, port, database, databaseConfig, useSSL);
|
||||
}
|
||||
|
||||
try {
|
||||
storage.createTables();
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
plugin.getLog().warn("Failed to setup database. Disabling plugin...", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkDriver(String className) {
|
||||
try {
|
||||
Class.forName(className);
|
||||
return true;
|
||||
} catch (ClassNotFoundException notFoundEx) {
|
||||
Logger log = plugin.getLog();
|
||||
log.warn("This driver {} is not supported on this platform", className);
|
||||
log.warn("Please choose MySQL (Spigot+BungeeCord), SQLite (Spigot+Sponge) or MariaDB (Sponge)", notFoundEx);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Configuration getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public PasswordGenerator<P> getPasswordGenerator() {
|
||||
return passwordGenerator;
|
||||
}
|
||||
|
||||
public void setPasswordGenerator(PasswordGenerator<P> passwordGenerator) {
|
||||
this.passwordGenerator = passwordGenerator;
|
||||
}
|
||||
|
||||
public ConcurrentMap<String, Object> getPendingLogin() {
|
||||
return pendingLogin;
|
||||
}
|
||||
|
||||
public Collection<UUID> getPendingConfirms() {
|
||||
return pendingConfirms;
|
||||
}
|
||||
|
||||
public AuthPlugin<P> getAuthPluginHook() {
|
||||
return authPlugin;
|
||||
}
|
||||
|
||||
public RateLimiter getRateLimiter() {
|
||||
return rateLimiter;
|
||||
}
|
||||
|
||||
public void setAuthPluginHook(AuthPlugin<P> authPlugin) {
|
||||
this.authPlugin = authPlugin;
|
||||
}
|
||||
|
||||
public void saveDefaultFile(String fileName) {
|
||||
Path dataFolder = plugin.getPluginFolder();
|
||||
|
||||
try {
|
||||
Files.createDirectories(dataFolder);
|
||||
|
||||
Path configFile = dataFolder.resolve(fileName);
|
||||
if (Files.notExists(configFile)) {
|
||||
try (InputStream defaultStream = getClass().getClassLoader().getResourceAsStream(fileName)) {
|
||||
Files.copy(Objects.requireNonNull(defaultStream), configFile);
|
||||
}
|
||||
}
|
||||
} catch (IOException ioExc) {
|
||||
plugin.getLog().error("Cannot create plugin folder {}", dataFolder, ioExc);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
plugin.getLog().info("Safely shutting down scheduler. This could take up to one minute.");
|
||||
plugin.getScheduler().shutdown();
|
||||
|
||||
if (storage != null) {
|
||||
storage.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.craftapi.model.Profile;
|
||||
import com.github.games647.craftapi.resolver.RateLimitException;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
public abstract class FloodgateManagement<P extends C, C, L extends LoginSession, T extends PlatformPlugin<C>>
|
||||
implements Runnable {
|
||||
|
||||
protected final FastLoginCore<P, C, T> core;
|
||||
protected final P player;
|
||||
private final FloodgatePlayer floodgatePlayer;
|
||||
private final String username;
|
||||
|
||||
//config.yml values that might be accessed by multiple methods
|
||||
protected final String autoLoginFloodgate;
|
||||
protected final String autoRegisterFloodgate;
|
||||
protected final String allowNameConflict;
|
||||
|
||||
//variables initialized through run() and accesses by subclasss
|
||||
protected boolean isRegistered;
|
||||
protected StoredProfile profile;
|
||||
protected boolean isLinked;
|
||||
|
||||
public FloodgateManagement(FastLoginCore<P, C, T> core, P player, FloodgatePlayer floodgatePlayer) {
|
||||
this.core = core;
|
||||
this.player = player;
|
||||
this.floodgatePlayer = floodgatePlayer;
|
||||
this.username = getName(player);
|
||||
|
||||
//load values from config.yml
|
||||
autoLoginFloodgate = core.getConfig().get("autoLoginFloodgate").toString().toLowerCase();
|
||||
autoRegisterFloodgate = core.getConfig().get("autoRegisterFloodgate").toString().toLowerCase();
|
||||
allowNameConflict = core.getConfig().get("allowFloodgateNameConflict").toString().toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
core.getPlugin().getLog().info("Player {} is connecting through Geyser Floodgate.", username);
|
||||
|
||||
// check if the Bedrock player is linked to a Java account
|
||||
isLinked = floodgatePlayer.getLinkedPlayer() != null;
|
||||
|
||||
//this happens on Bukkit if it's connected to Bungee
|
||||
//if that's the case, players will be logged in via plugin messages
|
||||
if (core.getStorage() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
profile = core.getStorage().loadProfile(username);
|
||||
AuthPlugin<P> authPlugin = core.getAuthPluginHook();
|
||||
|
||||
try {
|
||||
//maybe Bungee without auth plugin
|
||||
if (authPlugin == null) {
|
||||
if (profile != null) {
|
||||
isRegistered = profile.isPremium();
|
||||
} else {
|
||||
isRegistered = false;
|
||||
}
|
||||
} else {
|
||||
isRegistered = authPlugin.isRegistered(username);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
core.getPlugin().getLog().error(
|
||||
"An error has occured while checking if player {} is registered",
|
||||
username, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
//decide if checks should be made for conflicting Java player names
|
||||
if (isNameCheckRequired()) {
|
||||
// check for conflicting Premium Java name
|
||||
Optional<Profile> premiumUUID = Optional.empty();
|
||||
try {
|
||||
premiumUUID = core.getResolver().findProfile(username);
|
||||
} catch (IOException | RateLimitException e) {
|
||||
core.getPlugin().getLog().error(
|
||||
"Could not check whether Floodgate Player {}'s name conflicts a premium Java account's name.",
|
||||
username, e);
|
||||
return;
|
||||
}
|
||||
|
||||
//stop execution if player's name is conflicting
|
||||
if (premiumUUID.isPresent()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isRegistered && !isAutoRegisterAllowed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//logging in from bedrock for a second time threw an error with UUID
|
||||
if (profile == null) {
|
||||
profile = new StoredProfile(getUUID(player), username, true, getAddress(player).toString());
|
||||
}
|
||||
|
||||
//start Bukkit/Bungee specific tasks
|
||||
startLogin();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decude if the player can be auto registered.
|
||||
* The config option 'non-conflicting' is ignored by this function.
|
||||
* @return true if the Player can be registered automatically
|
||||
*/
|
||||
private boolean isAutoRegisterAllowed() {
|
||||
return "true".equals(autoRegisterFloodgate)
|
||||
|| "no-conflict".equals(autoRegisterFloodgate) // this was checked before
|
||||
|| ("linked".equals(autoRegisterFloodgate) && isLinked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides wether checks for conflicting Java names should be made
|
||||
* @return ture if an API call to Mojang is needed
|
||||
*/
|
||||
private boolean isNameCheckRequired() {
|
||||
//linked players have the same name as their Java profile
|
||||
//OR
|
||||
//if allowNameConflict is 'false' or 'linked' and the player had a conflicting
|
||||
//name, than they would have been kicked in FloodgateHook#checkNameConflict
|
||||
if (isLinked || !"true".equals(allowNameConflict)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//autoRegisterFloodgate should only be checked if then player is not yet registered
|
||||
if (!isRegistered && "no-conflict".equals(autoRegisterFloodgate)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return "no-conflict".equals(autoLoginFloodgate);
|
||||
}
|
||||
|
||||
protected abstract void startLogin();
|
||||
protected abstract String getName(P player);
|
||||
protected abstract UUID getUUID(P player);
|
||||
protected abstract InetSocketAddress getAddress(P player);
|
||||
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.fastlogin.core.storage.SQLStorage;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
|
||||
|
||||
public abstract class ForceLoginManagement<P extends C, C, L extends LoginSession, T extends PlatformPlugin<C>>
|
||||
implements Runnable {
|
||||
|
||||
protected final FastLoginCore<P, C, T> core;
|
||||
protected final P player;
|
||||
protected final L session;
|
||||
|
||||
public ForceLoginManagement(FastLoginCore<P, C, T> core, P player, L session) {
|
||||
this.core = core;
|
||||
this.player = player;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isOnline(player)) {
|
||||
core.getPlugin().getLog().info("Player {} disconnected", player);
|
||||
return;
|
||||
}
|
||||
|
||||
if (session == null) {
|
||||
core.getPlugin().getLog().info("No valid session found for {}", player);
|
||||
return;
|
||||
}
|
||||
|
||||
SQLStorage storage = core.getStorage();
|
||||
StoredProfile playerProfile = session.getProfile();
|
||||
try {
|
||||
if (isOnlineMode()) {
|
||||
//premium player
|
||||
AuthPlugin<P> authPlugin = core.getAuthPluginHook();
|
||||
if (authPlugin == null) {
|
||||
//maybe only bungeecord plugin
|
||||
onForceActionSuccess(session);
|
||||
} else {
|
||||
boolean success = true;
|
||||
String playerName = getName(player);
|
||||
if (core.getConfig().get("autoLogin", true)) {
|
||||
if (session.needsRegistration()
|
||||
|| (core.getConfig().get("auto-register-unknown", false)
|
||||
&& !authPlugin.isRegistered(playerName))) {
|
||||
success = forceRegister(player);
|
||||
} else if (!callFastLoginAutoLoginEvent(session, playerProfile).isCancelled()) {
|
||||
success = forceLogin(player);
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
//update only on success to prevent corrupt data
|
||||
if (playerProfile != null) {
|
||||
playerProfile.setId(session.getUuid());
|
||||
playerProfile.setPremium(true);
|
||||
storage.save(playerProfile);
|
||||
}
|
||||
|
||||
onForceActionSuccess(session);
|
||||
}
|
||||
}
|
||||
} else if (playerProfile != null) {
|
||||
//cracked player
|
||||
playerProfile.setId(null);
|
||||
playerProfile.setPremium(false);
|
||||
// LimeLogin start
|
||||
|
||||
//Govindas comment - don't save cracked player profiles, I found it to be useless
|
||||
//storage.save(playerProfile);
|
||||
//end of Govindas comment
|
||||
|
||||
// LimeLogin end
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
core.getPlugin().getLog().warn("ERROR ON FORCE LOGIN of {}", getName(player), ex);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean forceRegister(P player) {
|
||||
core.getPlugin().getLog().info("Register player {}", getName(player));
|
||||
|
||||
String generatedPassword = core.getPasswordGenerator().getRandomPassword(player);
|
||||
boolean success = core.getAuthPluginHook().forceRegister(player, generatedPassword);
|
||||
|
||||
String message = core.getMessage("auto-register");
|
||||
if (success && message != null) {
|
||||
message = message.replace("%password", generatedPassword);
|
||||
core.getPlugin().sendMessage(player, message);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public boolean forceLogin(P player) {
|
||||
core.getPlugin().getLog().info("Logging player {} in", getName(player));
|
||||
|
||||
boolean success = core.getAuthPluginHook().forceLogin(player);
|
||||
if (success) {
|
||||
core.sendLocaleMessage("auto-login", player);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public abstract FastLoginAutoLoginEvent callFastLoginAutoLoginEvent(LoginSession session, StoredProfile profile);
|
||||
|
||||
public abstract void onForceActionSuccess(LoginSession session);
|
||||
|
||||
public abstract String getName(P player);
|
||||
|
||||
public abstract boolean isOnline(P player);
|
||||
|
||||
public abstract boolean isOnlineMode();
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.craftapi.model.Profile;
|
||||
import com.github.games647.craftapi.resolver.RateLimitException;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import com.github.games647.fastlogin.core.hooks.FloodgateHook;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import com.github.games647.fastlogin.core.shared.event.LimePreLoginSecondAttemptEvent;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
|
||||
public abstract class JoinManagement<P extends C, C, S extends LoginSource> {
|
||||
|
||||
protected final FastLoginCore<P, C, ?> core;
|
||||
protected final AuthPlugin<P> authHook;
|
||||
private final FloodgateHook<P, C, ?> floodgateHook;
|
||||
|
||||
public JoinManagement(FastLoginCore<P, C, ?> core, AuthPlugin<P> authHook) {
|
||||
this.core = core;
|
||||
this.authHook = authHook;
|
||||
this.floodgateHook = new FloodgateHook<>(core);
|
||||
}
|
||||
|
||||
public void onLogin(String username, S source) {
|
||||
core.getPlugin().getLog().info("Handling player {}", username);
|
||||
StoredProfile profile = core.getStorage().loadProfile(username);
|
||||
if (profile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
//check if the player is connecting through Floodgate
|
||||
FloodgatePlayer floodgatePlayer = floodgateHook.getFloodgatePlayer(username);
|
||||
|
||||
if (floodgatePlayer != null) {
|
||||
floodgateHook.checkFloodgateNameConflict(username, source, floodgatePlayer);
|
||||
return;
|
||||
}
|
||||
callFastLoginPreLoginEvent(username, source, profile);
|
||||
|
||||
Configuration config = core.getConfig();
|
||||
|
||||
String ip = source.getAddress().getAddress().getHostAddress();
|
||||
profile.setLastIp(ip);
|
||||
try {
|
||||
if (profile.isSaved()) {
|
||||
if (profile.isPremium()) {
|
||||
core.getPlugin().getLog().info("Requesting premium login for registered player: {}", username);
|
||||
requestPremiumLogin(source, profile, username, true);
|
||||
} else {
|
||||
if (profile.getName().startsWith(FloodgateApi.getInstance().getPlayerPrefix())) {
|
||||
core.getPlugin().getLog().info("Floodgate Prefix detected on cracked player");
|
||||
source.kick("Your username contains illegal characters");
|
||||
return;
|
||||
}
|
||||
startCrackedSession(source, profile, username);
|
||||
}
|
||||
} else {
|
||||
if (core.getPendingLogin().remove(ip + username) != null && config.get("secondAttemptCracked", false)) {
|
||||
// LimeLogin start
|
||||
LimePreLoginSecondAttemptEvent event = new LimePreLoginSecondAttemptEvent(ip, username);
|
||||
ProxyServer.getInstance().getPluginManager().callEvent(event);
|
||||
if (!event.isCancelled()) {
|
||||
core.getPlugin().getLog().info("Second attempt login -> cracked {}", username);
|
||||
//first login request failed so make a cracked session
|
||||
startCrackedSession(source, profile, username);
|
||||
return;
|
||||
}
|
||||
// LimeLogin start
|
||||
}
|
||||
|
||||
Optional<Profile> premiumUUID = Optional.empty();
|
||||
if (config.get("nameChangeCheck", false) || config.get("autoRegister", false)) {
|
||||
premiumUUID = core.getResolver().findProfile(username);
|
||||
}
|
||||
|
||||
if (!premiumUUID.isPresent()
|
||||
|| (!checkNameChange(source, username, premiumUUID.get())
|
||||
&& !checkPremiumName(source, username, profile))) {
|
||||
//nothing detected the player as premium -> start a cracked session
|
||||
if (core.getConfig().get("switchMode", false)) {
|
||||
source.kick(core.getMessage("switch-kick-message"));
|
||||
return;
|
||||
}
|
||||
|
||||
startCrackedSession(source, profile, username);
|
||||
}
|
||||
}
|
||||
} catch (RateLimitException rateLimitEx) {
|
||||
core.getPlugin().getLog().error("Mojang's rate limit reached for {}. The public IPv4 address of this" +
|
||||
" server issued more than 600 Name -> UUID requests within 10 minutes. After those 10" +
|
||||
" minutes we can make requests again.", username);
|
||||
} catch (Exception ex) {
|
||||
core.getPlugin().getLog().error("Failed to check premium state for {}", username, ex);
|
||||
core.getPlugin().getLog().error("Failed to check premium state of {}", username, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkPremiumName(S source, String username, StoredProfile profile) throws Exception {
|
||||
core.getPlugin().getLog().info("GameProfile {} uses a premium username", username);
|
||||
// LimeLogin start
|
||||
|
||||
//start of Govindas comment, THIS IS NEEDED to be commented if FastLogin database gets emptied, to not get premium users counted as cracked
|
||||
//if (core.getConfig().get("autoRegister", false) && (authHook == null || !authHook.isRegistered(username))) {
|
||||
//end of Govindas comment
|
||||
|
||||
// LimeLogin end
|
||||
if (core.getConfig().get("autoRegister", false) && (authHook == null)) {
|
||||
// LimeLogin start
|
||||
requestPremiumLogin(source, profile, username, false); //Comment: always use false to fix an error XD
|
||||
// LimeLogin end
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkNameChange(S source, String username, Profile profile) {
|
||||
//user not exists in the db
|
||||
if (core.getConfig().get("nameChangeCheck", false)) {
|
||||
StoredProfile storedProfile = core.getStorage().loadProfile(profile.getId());
|
||||
if (storedProfile != null) {
|
||||
//uuid exists in the database
|
||||
core.getPlugin().getLog().info("GameProfile {} changed it's username", profile);
|
||||
|
||||
//update the username to the new one in the database
|
||||
storedProfile.setPlayerName(username);
|
||||
|
||||
requestPremiumLogin(source, storedProfile, username, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract FastLoginPreLoginEvent callFastLoginPreLoginEvent(String username, S source, StoredProfile profile);
|
||||
|
||||
public abstract void requestPremiumLogin(S source, StoredProfile profile, String username, boolean registered);
|
||||
|
||||
public abstract void startCrackedSession(S source, StoredProfile profile, String username);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class LoginSession {
|
||||
|
||||
private final StoredProfile profile;
|
||||
|
||||
private String requestUsername;
|
||||
private String username;
|
||||
private UUID uuid;
|
||||
|
||||
protected boolean registered;
|
||||
|
||||
public LoginSession(String requestUsername, boolean registered, StoredProfile profile) {
|
||||
this.requestUsername = requestUsername;
|
||||
this.username = requestUsername;
|
||||
|
||||
this.registered = registered;
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
public String getRequestUsername() {
|
||||
return requestUsername;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public synchronized void setVerifiedUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return This value is always false if we authenticate the player with a cracked authentication
|
||||
*/
|
||||
public synchronized boolean needsRegistration() {
|
||||
return !registered;
|
||||
}
|
||||
|
||||
public StoredProfile getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the premium UUID of this player
|
||||
*
|
||||
* @return the premium UUID or null if not fetched
|
||||
*/
|
||||
public synchronized UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the online UUID if it's fetched
|
||||
*
|
||||
* @param uuid premium UUID
|
||||
*/
|
||||
public synchronized void setUuid(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return Objects.toStringHelper(this)
|
||||
.add("profile", profile)
|
||||
.add("requestUsername", requestUsername)
|
||||
.add("username", username)
|
||||
.add("uuid", uuid)
|
||||
.add("registered", registered)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public interface LoginSource {
|
||||
|
||||
void enableOnlinemode() throws Exception;
|
||||
|
||||
void kick(String message) throws Exception;
|
||||
|
||||
InetSocketAddress getAddress();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.fastlogin.core.AsyncScheduler;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public interface PlatformPlugin<C> {
|
||||
|
||||
String getName();
|
||||
|
||||
Path getPluginFolder();
|
||||
|
||||
Logger getLog();
|
||||
|
||||
void sendMessage(C receiver, String message);
|
||||
|
||||
AsyncScheduler getScheduler();
|
||||
|
||||
boolean isPluginInstalled(String name);
|
||||
|
||||
default void sendMultiLineMessage(C receiver, String message) {
|
||||
for (String line : message.split("%nl%")) {
|
||||
sendMessage(receiver, line);
|
||||
}
|
||||
}
|
||||
|
||||
default ThreadFactory getThreadFactory() {
|
||||
return new ThreadFactoryBuilder()
|
||||
.setNameFormat(getName() + " Pool Thread #%1$d")
|
||||
// Hikari create daemons by default and we could daemon threads for our own scheduler too
|
||||
// because we safely shutdown
|
||||
.setDaemon(true)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.shared.event;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
|
||||
public interface FastLoginAutoLoginEvent extends FastLoginCancellableEvent {
|
||||
LoginSession getSession();
|
||||
StoredProfile getProfile();
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.shared.event;
|
||||
|
||||
public interface FastLoginCancellableEvent {
|
||||
|
||||
boolean isCancelled();
|
||||
void setCancelled(boolean cancelled);
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.shared.event;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
||||
|
||||
public interface FastLoginPreLoginEvent {
|
||||
|
||||
String getUsername();
|
||||
LoginSource getSource();
|
||||
StoredProfile getProfile();
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.shared.event;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
|
||||
public interface FastLoginPremiumToggleEvent {
|
||||
|
||||
StoredProfile getProfile();
|
||||
PremiumToggleReason getReason();
|
||||
|
||||
enum PremiumToggleReason {
|
||||
COMMAND_SELF,
|
||||
COMMAND_OTHER
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// LimeLogin start
|
||||
package com.github.games647.fastlogin.core.shared.event;
|
||||
|
||||
import net.md_5.bungee.api.plugin.Cancellable;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
public class LimePreLoginSecondAttemptEvent extends Event implements Cancellable {
|
||||
|
||||
private final String ip;
|
||||
private final String username;
|
||||
private boolean cancelled;
|
||||
|
||||
public LimePreLoginSecondAttemptEvent(String ip, String username) {
|
||||
this.ip = ip;
|
||||
this.username = username;
|
||||
}
|
||||
public String getIP() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
}
|
||||
// LimeLogin end
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.github.games647.fastlogin.core.storage;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface AuthStorage {
|
||||
StoredProfile loadProfile(String name);
|
||||
|
||||
StoredProfile loadProfile(UUID uuid);
|
||||
|
||||
void save(StoredProfile playerProfile);
|
||||
|
||||
void close();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.github.games647.fastlogin.core.storage;
|
||||
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
|
||||
public class MySQLStorage extends SQLStorage {
|
||||
|
||||
public MySQLStorage(FastLoginCore<?, ?, ?> core, String host, int port, String database, HikariConfig config,
|
||||
boolean useSSL) {
|
||||
super(core,
|
||||
"mysql://" + host + ':' + port + '/' + database,
|
||||
setParams(config, useSSL));
|
||||
}
|
||||
|
||||
private static HikariConfig setParams(HikariConfig config, boolean useSSL) {
|
||||
// Require SSL on the server if requested in config - this will also verify certificate
|
||||
// Those values are deprecated in favor of sslMode
|
||||
config.addDataSourceProperty("useSSL", useSSL);
|
||||
config.addDataSourceProperty("requireSSL", useSSL);
|
||||
|
||||
// adding paranoid hides hostname, username, version and so
|
||||
// could be useful for hiding server details
|
||||
config.addDataSourceProperty("paranoid", true);
|
||||
|
||||
// enable MySQL specific optimizations
|
||||
addPerformanceProperties(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
private static void addPerformanceProperties(HikariConfig config) {
|
||||
// disabled by default - will return the same prepared statement instance
|
||||
config.addDataSourceProperty("cachePrepStmts", true);
|
||||
// default prepStmtCacheSize 25 - amount of cached statements
|
||||
config.addDataSourceProperty("prepStmtCacheSize", 250);
|
||||
// default prepStmtCacheSqlLimit 256 - length of SQL
|
||||
config.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
|
||||
// default false - available in newer versions caches the statements server-side
|
||||
config.addDataSourceProperty("useServerPrepStmts", true);
|
||||
// default false - prefer use of local values for autocommit and
|
||||
// transaction isolation (alwaysSendSetIsolation) should only be enabled if always use the set* methods
|
||||
// instead of raw SQL
|
||||
// https://forums.mysql.com/read.php?39,626495,626512
|
||||
config.addDataSourceProperty("useLocalSessionState", true);
|
||||
// rewrite batched statements to a single statement, adding them behind each other
|
||||
// only useful for addBatch statements and inserts
|
||||
config.addDataSourceProperty("rewriteBatchedStatements", true);
|
||||
// cache result metadata
|
||||
config.addDataSourceProperty("cacheResultSetMetadata", true);
|
||||
// cache results of show variables and collation per URL
|
||||
config.addDataSourceProperty("cacheServerConfiguration", true);
|
||||
// default false - set auto commit only if not matching
|
||||
config.addDataSourceProperty("elideSetAutoCommits", true);
|
||||
|
||||
// default true - internal timers for idle calculation -> removes System.getCurrentTimeMillis call per query
|
||||
// Some platforms are slow on this and it could affect the throughput about 3% according to MySQL
|
||||
// performance gems presentation
|
||||
// In our case it can be useful to see the time in error messages
|
||||
// config.addDataSourceProperty("maintainTimeStats", false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.storage;
|
||||
|
||||
import com.github.games647.craftapi.UUIDAdapter;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import static java.sql.Statement.RETURN_GENERATED_KEYS;
|
||||
|
||||
public abstract class SQLStorage implements AuthStorage {
|
||||
|
||||
private static final String JDBC_PROTOCOL = "jdbc:";
|
||||
|
||||
protected static final String PREMIUM_TABLE = "premium";
|
||||
protected static final String CREATE_TABLE_STMT = "CREATE TABLE IF NOT EXISTS `" + PREMIUM_TABLE + "` ("
|
||||
+ "`UserID` INTEGER PRIMARY KEY AUTO_INCREMENT, "
|
||||
+ "`UUID` CHAR(36), "
|
||||
+ "`Name` VARCHAR(16) NOT NULL, "
|
||||
+ "`Premium` BOOLEAN NOT NULL, "
|
||||
+ "`LastIp` VARCHAR(255) NOT NULL, "
|
||||
+ "`LastLogin` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
|
||||
//the premium shouldn't steal the cracked account by changing the name
|
||||
+ "UNIQUE (`Name`) "
|
||||
+ ')';
|
||||
|
||||
protected static final String LOAD_BY_NAME = "SELECT * FROM `" + PREMIUM_TABLE + "` WHERE `Name`=? LIMIT 1";
|
||||
protected static final String LOAD_BY_UUID = "SELECT * FROM `" + PREMIUM_TABLE + "` WHERE `UUID`=? LIMIT 1";
|
||||
protected static final String INSERT_PROFILE = "INSERT INTO `" + PREMIUM_TABLE
|
||||
+ "` (`UUID`, `Name`, `Premium`, `LastIp`) " + "VALUES (?, ?, ?, ?) ";
|
||||
// limit not necessary here, because it's unique
|
||||
protected static final String UPDATE_PROFILE = "UPDATE `" + PREMIUM_TABLE
|
||||
+ "` SET `UUID`=?, `Name`=?, `Premium`=?, `LastIp`=?, `LastLogin`=CURRENT_TIMESTAMP WHERE `UserID`=?";
|
||||
|
||||
protected final FastLoginCore<?, ?, ?> core;
|
||||
protected final HikariDataSource dataSource;
|
||||
|
||||
public SQLStorage(FastLoginCore<?, ?, ?> core, String jdbcURL, HikariConfig config) {
|
||||
this.core = core;
|
||||
config.setPoolName(core.getPlugin().getName());
|
||||
|
||||
ThreadFactory platformThreadFactory = core.getPlugin().getThreadFactory();
|
||||
if (platformThreadFactory != null) {
|
||||
config.setThreadFactory(platformThreadFactory);
|
||||
}
|
||||
|
||||
config.setJdbcUrl(JDBC_PROTOCOL + jdbcURL);
|
||||
this.dataSource = new HikariDataSource(config);
|
||||
}
|
||||
|
||||
public void createTables() throws SQLException {
|
||||
// choose surrogate PK(ID), because UUID can be null for offline players
|
||||
// if UUID is always Premium UUID we would have to update offline player entries on insert
|
||||
// name cannot be PK, because it can be changed for premium players
|
||||
String createDataStmt = "CREATE TABLE IF NOT EXISTS `" + PREMIUM_TABLE + "` ("
|
||||
+ "`UserID` INTEGER PRIMARY KEY AUTO_INCREMENT, "
|
||||
+ "`UUID` CHAR(36), "
|
||||
+ "`Name` VARCHAR(16) NOT NULL, "
|
||||
+ "`Premium` BOOLEAN NOT NULL, "
|
||||
+ "`LastIp` VARCHAR(255) NOT NULL, "
|
||||
+ "`LastLogin` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAM"
|
||||
//the premium shouldn't steal the cracked account by changing the name
|
||||
+ ')';
|
||||
|
||||
if (dataSource.getJdbcUrl().contains("sqlite")) {
|
||||
createDataStmt = createDataStmt.replace("AUTO_INCREMENT", "AUTOINCREMENT");
|
||||
}
|
||||
//todo: add unique uuid index usage
|
||||
try (Connection con = dataSource.getConnection();
|
||||
Statement createStmt = con.createStatement()) {
|
||||
createStmt.executeUpdate(CREATE_TABLE_STMT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredProfile loadProfile(String name) {
|
||||
try (Connection con = dataSource.getConnection();
|
||||
PreparedStatement loadStmt = con.prepareStatement(LOAD_BY_NAME)
|
||||
) {
|
||||
loadStmt.setString(1, name);
|
||||
|
||||
try (ResultSet resultSet = loadStmt.executeQuery()) {
|
||||
return parseResult(resultSet).orElseGet(() -> new StoredProfile(null, name, false, ""));
|
||||
}
|
||||
} catch (SQLException sqlEx) {
|
||||
core.getPlugin().getLog().error("Failed to query profile: {}", name, sqlEx);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredProfile loadProfile(UUID uuid) {
|
||||
try (Connection con = dataSource.getConnection();
|
||||
PreparedStatement loadStmt = con.prepareStatement(LOAD_BY_UUID)) {
|
||||
loadStmt.setString(1, UUIDAdapter.toMojangId(uuid));
|
||||
|
||||
try (ResultSet resultSet = loadStmt.executeQuery()) {
|
||||
return parseResult(resultSet).orElse(null);
|
||||
}
|
||||
} catch (SQLException sqlEx) {
|
||||
core.getPlugin().getLog().error("Failed to query profile: {}", uuid, sqlEx);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Optional<StoredProfile> parseResult(ResultSet resultSet) throws SQLException {
|
||||
if (resultSet.next()) {
|
||||
long userId = resultSet.getInt(1);
|
||||
|
||||
UUID uuid = Optional.ofNullable(resultSet.getString(2)).map(UUIDAdapter::parseId).orElse(null);
|
||||
|
||||
String name = resultSet.getString(3);
|
||||
boolean premium = resultSet.getBoolean(4);
|
||||
String lastIp = resultSet.getString(5);
|
||||
Instant lastLogin = resultSet.getTimestamp(6).toInstant();
|
||||
return Optional.of(new StoredProfile(userId, uuid, name, premium, lastIp, lastLogin));
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(StoredProfile playerProfile) {
|
||||
try (Connection con = dataSource.getConnection()) {
|
||||
String uuid = playerProfile.getOptId().map(UUIDAdapter::toMojangId).orElse(null);
|
||||
// LimeLogin start
|
||||
|
||||
//start of Govindas code
|
||||
if (!playerProfile.getName().contains("-")) { playerProfile.setPremium(true); }
|
||||
//end of Govindas code
|
||||
|
||||
// LimeLogin end
|
||||
playerProfile.getSaveLock().lock();
|
||||
try {
|
||||
if (playerProfile.isSaved()) {
|
||||
try (PreparedStatement saveStmt = con.prepareStatement(UPDATE_PROFILE)) {
|
||||
saveStmt.setString(1, uuid);
|
||||
saveStmt.setString(2, playerProfile.getName());
|
||||
saveStmt.setBoolean(3, playerProfile.isPremium());
|
||||
saveStmt.setString(4, playerProfile.getLastIp());
|
||||
|
||||
saveStmt.setLong(5, playerProfile.getRowId());
|
||||
saveStmt.execute();
|
||||
}
|
||||
} else {
|
||||
// LimeLogin start
|
||||
|
||||
//start of Govindas code
|
||||
if (!playerProfile.getName().contains("-")) { playerProfile.setPremium(true); }
|
||||
//end of Govindas code
|
||||
|
||||
// LimeLogin end
|
||||
try (PreparedStatement saveStmt = con.prepareStatement(INSERT_PROFILE, RETURN_GENERATED_KEYS)) {
|
||||
saveStmt.setString(1, uuid);
|
||||
|
||||
saveStmt.setString(2, playerProfile.getName());
|
||||
saveStmt.setBoolean(3, playerProfile.isPremium());
|
||||
saveStmt.setString(4, playerProfile.getLastIp());
|
||||
|
||||
saveStmt.execute();
|
||||
try (ResultSet generatedKeys = saveStmt.getGeneratedKeys()) {
|
||||
if (generatedKeys.next()) {
|
||||
playerProfile.setRowId(generatedKeys.getInt(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
playerProfile.getSaveLock().unlock();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
core.getPlugin().getLog().error("Failed to save playerProfile {}", playerProfile, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
dataSource.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.github.games647.fastlogin.core.storage;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class SQLiteStorage extends SQLStorage {
|
||||
|
||||
private final Lock lock = new ReentrantLock();
|
||||
|
||||
public SQLiteStorage(FastLoginCore<?, ?, ?> core, String databasePath, HikariConfig config) {
|
||||
super(core,
|
||||
"sqlite://" + replacePathVariables(core.getPlugin(), databasePath),
|
||||
setParams(config));
|
||||
}
|
||||
|
||||
private static HikariConfig setParams(HikariConfig config) {
|
||||
config.setConnectionTestQuery("SELECT 1");
|
||||
config.setMaximumPoolSize(1);
|
||||
|
||||
//a try to fix https://www.spigotmc.org/threads/fastlogin.101192/page-26#post-1874647
|
||||
// format strings retrieved by the timestamp column to match them from MySQL
|
||||
config.addDataSourceProperty("date_string_format", "yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// TODO: test first for compatibility
|
||||
// config.addDataSourceProperty("date_precision", "seconds");
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredProfile loadProfile(String name) {
|
||||
lock.lock();
|
||||
try {
|
||||
return super.loadProfile(name);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredProfile loadProfile(UUID uuid) {
|
||||
lock.lock();
|
||||
try {
|
||||
return super.loadProfile(uuid);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(StoredProfile playerProfile) {
|
||||
lock.lock();
|
||||
try {
|
||||
super.save(playerProfile);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createTables() throws SQLException {
|
||||
try (Connection con = dataSource.getConnection();
|
||||
Statement createStmt = con.createStatement()) {
|
||||
// SQLite has a different syntax for auto increment
|
||||
createStmt.executeUpdate(CREATE_TABLE_STMT.replace("AUTO_INCREMENT", "AUTOINCREMENT"));
|
||||
}
|
||||
}
|
||||
|
||||
private static String replacePathVariables(PlatformPlugin<?> plugin, String input) {
|
||||
String pluginFolder = plugin.getPluginFolder().toAbsolutePath().toString();
|
||||
return input.replace("{pluginDir}", pluginFolder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
# FastLogin config
|
||||
# Project site: https://www.spigotmc.org/resources/fastlogin.14153
|
||||
# Source code: https://github.com/games647/FastLogin
|
||||
#
|
||||
# You can access the newest config here:
|
||||
# https://github.com/games647/FastLogin/blob/main/core/src/main/resources/config.yml
|
||||
|
||||
# This a **very** simple anti bot protection. Recommendation is to use a a dedicated program to approach this
|
||||
# problem. Low level firewalls like uwf (or iptables direct) are more efficient than a Minecraft plugin. TCP reverse
|
||||
# proxies could also be used and offload some work even to different host.
|
||||
#
|
||||
# The settings wil limit how many connections this plugin will handle. After hitting this limit. FastLogin will
|
||||
# completely ignore incoming connections. Effectively there will be no database requests and network requests.
|
||||
# Therefore auto logins won't be possible.
|
||||
anti-bot:
|
||||
# Image the following like bucket. The following is total amount that is allowed in this bucket, while expire
|
||||
# means how long it takes for every entry to expire.
|
||||
# Total number of connections
|
||||
connections: 600
|
||||
# Amount of minutes after the first connection got inserted will expire and made available
|
||||
expire: 10
|
||||
|
||||
# Request a premium login without forcing the player to type a command
|
||||
#
|
||||
# If you activate autoRegister, this plugin will check/do these points on login:
|
||||
# 1. An existing cracked account shouldn't exist
|
||||
# -> paid accounts cannot steal the existing account of cracked players
|
||||
# - (Already registered players could still use the /premium command to activate premium checks)
|
||||
# 2. Automatically registers an account with a strong random generated password
|
||||
# -> cracked player cannot register an account for the premium player and so cannot the steal the account
|
||||
#
|
||||
# Furthermore the premium player check have to be made based on the player name
|
||||
# This means if a cracked player connects to the server and we request a paid account login from this player
|
||||
# the player just disconnect and sees the message: 'bad login' or 'invalid session'
|
||||
# There is no way to change this message
|
||||
# For more information: https://github.com/games647/FastLogin#why-do-players-have-to-invoke-a-command
|
||||
autoRegister: false
|
||||
|
||||
# Should FastLogin respect per IP limit of registrations (e.g. in AuthMe)
|
||||
# Because most auth plugins do their stuff async - FastLogin will still think the player was registered
|
||||
# To work best - you also need to enable auto-register-unknown
|
||||
#
|
||||
# If set to true - FastLogin will always attempt to register the player, even if the limit is exceeded
|
||||
# It is up to the auth plugin to handle the excessive registration
|
||||
# https://github.com/games647/FastLogin/issues/458
|
||||
respectIpLimit: false
|
||||
|
||||
# This is extra configuration option to the feature above. If we request a premium authentication from a player who
|
||||
# isn't actual premium but used a premium username, the player will disconnect with the reason "invalid session" or
|
||||
# "bad login".
|
||||
#
|
||||
# If you activate this, we are remembering this player and do not force another premium authentication if the player
|
||||
# tries to join again, so the player could join as cracked player.
|
||||
secondAttemptCracked: false
|
||||
|
||||
# New cracked players will be kicked from server. Good if you want switch from offline-mode to online-mode without
|
||||
# losing players!
|
||||
#
|
||||
# Existing cracked and premium players could still join your server. Moreover you could add playernames to a
|
||||
# allowlist.
|
||||
# So that these cracked players could join too although they are new players.
|
||||
switchMode: false
|
||||
|
||||
# If this plugin detected that a player has a premium, it can also set the associated
|
||||
# uuid from that account. So if the player changes the username, they will still have
|
||||
# the same player data (inventory, permissions, ...)
|
||||
#
|
||||
# Warning: This also means that the UUID will be different if the player is connecting
|
||||
# through a offline mode connection. This **could** cause plugin compatibility issues.
|
||||
#
|
||||
# This is a example and doesn't apply for every plugin.
|
||||
# Example: If you want to ban players who aren't online at the moment, the ban plugin will look
|
||||
# after a offline uuid associated to the player, because the server is in offline mode. Then the premium
|
||||
# players could still join the server, because they have different UUID.
|
||||
#
|
||||
# Moreover you may want to convert the offline UUID to a premium UUID. This will ensure that the player
|
||||
# will have the same inventory, permissions, ... if they switched to premium authentication from offline/cracked
|
||||
# authentication.
|
||||
#
|
||||
# This feature requires Cauldron, Spigot or a fork of Spigot (Paper)
|
||||
premiumUuid: false
|
||||
|
||||
# This will make an additional check (only for player names which are not in the database) against the mojang servers
|
||||
# in order to get the premium UUID. If that premium UUID is in the database, we can assume on successful login that the
|
||||
# player changed it's username and we just update the name in the database.
|
||||
# Examples:
|
||||
# #### Case 1
|
||||
# autoRegister = false
|
||||
# nameChangeCheck = false
|
||||
#
|
||||
# GameProfile logins as cracked until the player invoked the command /premium. Then we could override the existing
|
||||
# database record.
|
||||
#
|
||||
# #### Case 2
|
||||
# autoRegister = false
|
||||
# nameChangeCheck = true
|
||||
#
|
||||
# Connect the Mojang API and check what UUID the player has (UUID exists => Paid Minecraft account). If that UUID is in
|
||||
# the database it's an **existing player** and FastLogin can **assume** the player is premium and changed the username.
|
||||
# If it's not in the database, it's a new player and **could be a cracked player**. So we just use a offline mode
|
||||
# authentication for this player.
|
||||
#
|
||||
# **Limitation**: Cracked players who uses the new username of a paid account cannot join the server if the database
|
||||
# contains the old name. (Example: The owner of the paid account no longer plays on the server, but changed the username
|
||||
# in the meanwhile).
|
||||
#
|
||||
# #### Case 3
|
||||
# autoRegister = true
|
||||
# nameChangeCheck = false
|
||||
#
|
||||
# We will always request a premium authentication if the username is unknown to us, but is in use by a paid Minecraft
|
||||
# account. This means it's kind of a more aggressive check like nameChangeCheck = true and autoRegister = false, because
|
||||
# it request a premium authentication which are completely new to us, that even the premium UUID is not in our database.
|
||||
#
|
||||
# **Limitation**: see below
|
||||
#
|
||||
# #### Case 4
|
||||
# autoRegister = true
|
||||
# nameChangeCheck = true
|
||||
#
|
||||
# Based on autoRegister it checks if the player name is premium and login using a premium authentication. After that
|
||||
# fastlogin receives the premium UUID and can update the database record.
|
||||
#
|
||||
# **Limitation from autoRegister**: New offline players who uses the username of an existing Minecraft cannot join the
|
||||
# server.
|
||||
nameChangeCheck: false
|
||||
|
||||
# If your players have a premium account and a skin associated to their account, this plugin
|
||||
# can download the data and set it to the online player.
|
||||
#
|
||||
# Keep in mind that this will only works if the player:
|
||||
# * is the owner of the premium account
|
||||
# * the server connection is established through a premium connection (paid account authentication)
|
||||
# * has a skin
|
||||
#
|
||||
# This means this plugin doesn't need to create a new connection to the Mojang servers, because
|
||||
# the skin data is included in the Auth-Verification-Response sent by Mojang. If you want to use for other
|
||||
# players like cracked player, you have to use other plugins.
|
||||
#
|
||||
# If you use PaperSpigot - FastLogin will always try to set the skin, even if forwardSkin is set to false
|
||||
# It is needed to allow premium name change to work correctly
|
||||
# https://github.com/games647/FastLogin/issues/457
|
||||
#
|
||||
# If you want to use skins for your cracked player, you need an additional plugin like
|
||||
# ChangeSkin, SkinRestorer, ...
|
||||
forwardSkin: true
|
||||
|
||||
# Displays a warning message that this message SHOULD only be invoked by
|
||||
# users who actually are the owner of this account. So not by cracked players
|
||||
#
|
||||
# If they still want to invoke the command, they have to invoke /premium again
|
||||
premium-warning: true
|
||||
|
||||
# If you have autoRegister or nameChangeCheck enabled, you could be rate-limited by Mojang.
|
||||
# The requests of the both options will be only made by FastLogin if the username is unknown to the server
|
||||
# You are allowed to make 600 requests per 10-minutes (60 per minute)
|
||||
# If you own a big server this value could be too low
|
||||
# Once the limit is reached, new players are always logged in as cracked until the rate-limit is expired.
|
||||
# (to the next ten minutes)
|
||||
#
|
||||
# The limit is IP-wide. If you have multiple IPv4-addresses you specify them here. FastLogin will then use it in
|
||||
# rotating order --> 5 different IP-addresses 5 * 600 per 10 minutes
|
||||
# If this list is empty only the default one will be used
|
||||
#
|
||||
# Lists are created like this:
|
||||
#ip-addresses:
|
||||
# - 192-168-0-2
|
||||
ip-addresses: []
|
||||
|
||||
# How many requests should be established to the Mojang API for Name -> UUID requests. Some other plugins as well
|
||||
# as the head Minecraft block make such requests as well. Using this option you can limit the amount requests this
|
||||
# plugin should make.
|
||||
#
|
||||
# If you lower this value, other plugins could still make requests while FastLogin cannot.
|
||||
# Mojang limits the amount of request to 600 per 10 minutes per IPv4-address.
|
||||
mojang-request-limit: 600
|
||||
|
||||
# This option automatically registers players which are in the FastLogin database, but not in the auth plugin database.
|
||||
# This can happen if you switch your auth plugin or cleared the database of the auth plugin.
|
||||
# https://github.com/games647/FastLogin/issues/85
|
||||
auto-register-unknown: false
|
||||
|
||||
# This disables the auto login from fastlogin. So a premium (like a paid account) authentication is requested, but
|
||||
# the player won't be auto logged into the account.
|
||||
#
|
||||
# This can be used as 2Factor authentication for better security of your accounts. A hacker then needs both passwords.
|
||||
# The password of your Minecraft and the password to login in with your auth plugin
|
||||
autoLogin: true
|
||||
|
||||
# Floodgate configuration
|
||||
# Connecing through Floodgate requires player's to sign in via their Xbox Live account
|
||||
# !!!!!!!! WARNING: FLOODGATE SUPPORT IS AN EXPERIMENTAL FEATURE !!!!!!!!
|
||||
# Enabling any of these settings might lead to people gaining unauthorized access to other's accounts!
|
||||
|
||||
# Automatically log in players connecting through Floodgate.
|
||||
# Possible values:
|
||||
# false: Disables auto login for every player connecting through Floodgate
|
||||
# true: Enables auto login for every player connecting through Floodgate
|
||||
# linked: Only Bedrock accounts that are linked to a Java account will be logged in automatically
|
||||
# no-conflict: Bedrock players will only be automatically logged in if the Mojang API reports
|
||||
# that there is no existing Premium Java MC account with their name.
|
||||
# This option can be useful if you are not using 'username-prefix' in floodgate/config.yml
|
||||
# Requires 'autoLogin' to be 'true'
|
||||
# !!!!!!!! WARNING: FLOODGATE SUPPORT IS AN EXPERIMENTAL FEATURE !!!!!!!!
|
||||
# Enabling this might lead to people gaining unauthorized access to other's accounts!
|
||||
autoLoginFloodgate: false
|
||||
|
||||
# This enables Floodgate players to join the server, even if autoRegister is true and there's an existing
|
||||
# Java **PREMIUM** account with the same name
|
||||
#
|
||||
# Java and Bedrock players will get different UUIDs, so their inventories, location, etc. will be different.
|
||||
# However, some plugins (such as AuthMe) rely on names instead of UUIDs to identify a player which might cause issues.
|
||||
# In the case of AuthMe (and other auth plugins), both the Java and the Bedrock player will have the same password.
|
||||
#
|
||||
# To prevent conflits from two different players having the same name, it is highly recommended to use a 'username-prefix'
|
||||
# in floodgate/config.yml
|
||||
# Note: 'username-prefix' is currently broken when used with FastLogin and ProtocolLib. For more information visit:
|
||||
# https://github.com/games647/FastLogin/issues/493
|
||||
# A solution to this is to replace ProtocolLib with ProtocolSupport
|
||||
#
|
||||
# Possible values:
|
||||
# false: Check for Premium Java name conflicts as described in 'autoRegister'
|
||||
# Note: Linked players have the same name as their Java profile, so the Bedrock player will always conflict
|
||||
# their own Java account's name. Therefore, setting this to false will prevent any linked player from joining.
|
||||
# true: Bypass 'autoRegister's name conflict checking
|
||||
# linked: Bedrock accounts linked to a Java account will be allowed to join with conflicting names
|
||||
# !!!!!!!! WARNING: FLOODGATE SUPPORT IS AN EXPERIMENTAL FEATURE !!!!!!!!
|
||||
# Enabling this might lead to people gaining unauthorized access to other's accounts!
|
||||
allowFloodgateNameConflict: false
|
||||
|
||||
# Automatically register players connecting through Floodgate.
|
||||
# autoLoginFloodgate must be available for the player to use this
|
||||
# Possible values:
|
||||
# false: Disables auto registering for every player connecting through Floodgate
|
||||
# true: Enables auto registering for every player connecting through Floodgate
|
||||
# linked: Only Bedrock accounts that are linked to a Java account will be registered automatically
|
||||
# no-conflict: Bedrock players will only be automatically registered if the Mojang API reports
|
||||
# that there is no existing Premium Java MC account with their name.
|
||||
# This option can be useful if you are not using 'username-prefix' in floodgate/config.yml
|
||||
# Requires 'autoRegister' to be 'true'
|
||||
# !!!!!!!! WARNING: FLOODGATE SUPPORT IS AN EXPERIMENTAL FEATURE !!!!!!!!
|
||||
# Enabling this might lead to people gaining unauthorized access to other's accounts!
|
||||
autoRegisterFloodgate: false
|
||||
|
||||
# Database configuration
|
||||
# Recommended is the use of MariaDB (a better version of MySQL)
|
||||
|
||||
# Single file SQLite database
|
||||
driver: 'fastlogin.sqlite.JDBC'
|
||||
# File location
|
||||
database: '{pluginDir}/FastLogin.db'
|
||||
|
||||
# MySQL/MariaDB
|
||||
# If you want to enable it uncomment only the lines below this not this line.
|
||||
# If on velocity use 'fastlogin.mysql.cj.jdbc.Driver' as driver
|
||||
#driver: 'com.mysql.jdbc.Driver'
|
||||
#host: '127.0.0.1'
|
||||
#port: 3306
|
||||
#database: 'fastlogin'
|
||||
#username: 'myUser'
|
||||
#password: 'myPassword'
|
||||
|
||||
# Advanced Connection Pool settings in seconds
|
||||
#timeout: 30
|
||||
#lifetime: 30
|
||||
|
||||
## It's recommended to enable SSL if the MySQL server isn't running on the same host
|
||||
## This will encrypt the connection for secure transportation of the sql server password
|
||||
#useSSL: false
|
||||
## Verification requirements for the server cert,
|
||||
## Values: Required (unchecked SSL connection), VerifyCA (verify CA), VerifyFull (verify CA and matching hostname)
|
||||
#sslMode=Required
|
||||
## TLS is preferred for this technique, then your host stored certificate store will be used to verify the server cert
|
||||
## Similar to HTTPS. If that's not possible RSA can be used with the following options.
|
||||
## This allows to request the public RSA key from the server to encrypt the data to it. True would allow machine-in-the-
|
||||
## middle attacks.
|
||||
#allowPublicKeyRetrieval=false
|
||||
## Path to the RSA public key if key retrieval is forbidden
|
||||
#ServerRSAPublicKeyFile=
|
||||
|
||||
# HTTP proxies for connecting to the Mojang servers in order to check if the username of a player is premium.
|
||||
# This is a workaround to prevent rate-limiting by Mojang. These proxies will only be used once your server hit
|
||||
# the rate-limit or the custom value above.
|
||||
# Please make sure you use reliable proxies.
|
||||
proxies:
|
||||
# 'IP:Port' or 'Domain:Port'
|
||||
# - 'xyz.com:1337'
|
||||
# - 'test.com:5131'
|
||||
@@ -0,0 +1,98 @@
|
||||
# FastLogin localization
|
||||
# Project site: https://www.spigotmc.org/resources/fastlogin.14153
|
||||
# Source code: https://github.com/games647/FastLogin
|
||||
#
|
||||
# You can access the newest locale here:
|
||||
# https://github.com/games647/FastLogin/blob/main/core/src/main/resources/messages.yml
|
||||
#
|
||||
# You want to have language template? Visit the GitHub Wiki here:
|
||||
# https://github.com/games647/FastLogin/wiki/English
|
||||
|
||||
# In order to split a message into separate lines you could just make a new line, but keep the '
|
||||
# Example:
|
||||
# bla: '&aFirst line
|
||||
# Second line
|
||||
# Third line'
|
||||
|
||||
# If you want to disable a message, you can just set it to a empty value.
|
||||
# In this case no message will be sent
|
||||
# Example:
|
||||
# bla: ''
|
||||
|
||||
# ========= Shared (BungeeCord and Bukkit) ============
|
||||
|
||||
# Switch mode is activated and a new cracked player tries to join, who is not namely allowed
|
||||
switch-kick-message: '&4Only paid Minecraft allowed accounts are allowed to join this server'
|
||||
|
||||
# GameProfile activated premium login in order to skip offline authentication
|
||||
add-premium: '&2Added to the list of premium players'
|
||||
|
||||
# GameProfile activated premium login in order to skip offline authentication
|
||||
add-premium-other: '&2Player has been added to the premium list'
|
||||
|
||||
# GameProfile is already set be a paid account
|
||||
already-exists: '&4You are already on the premium list'
|
||||
|
||||
# GameProfile is already set be a paid account
|
||||
already-exists-other: '&4Player is already on the premium list'
|
||||
|
||||
# GameProfile was changed to be cracked
|
||||
remove-premium: '&2Removed from the list of premium players'
|
||||
|
||||
# GameProfile is already set to be cracked
|
||||
not-premium: '&4You are not in the premium list'
|
||||
|
||||
# GameProfile is already set to be cracked
|
||||
not-premium-other: '&4Player is not in the premium list'
|
||||
|
||||
# Admin wanted to change the premium of a user that isn't known to the plugin
|
||||
player-unknown: '&4Player not in the database'
|
||||
|
||||
# ========= Bukkit/Spigot ================
|
||||
|
||||
# The user skipped the authentication, because it was a premium player
|
||||
auto-login: '&2Auto logged in'
|
||||
|
||||
# FastLogin attempted to auto register user. The user account is registered to protect it from cracked players
|
||||
# If FastLogin is respecting auth plugin IP limit - the registration may have failed, however the message is still displayed
|
||||
# The password can be used if the mojang servers are down and you still want your premium users to login (PLANNED)
|
||||
auto-register: '&2Tried auto registering with password: &7%password&2. You may want change it?'
|
||||
|
||||
# GameProfile is not able to toggle the premium state of other players
|
||||
no-permission: '&4Not enough permissions'
|
||||
|
||||
# Although the console can toggle the premium state, it's not possible for the console itself.
|
||||
# Because the console is not a user. (obviously, isn't it?)
|
||||
no-console: '&4You are not a player. You cannot toggle the premium state for YOURSELF as a console'
|
||||
|
||||
# The user wants to toggle premium state, but BungeeCord support is enabled. This means the server have to communicate
|
||||
# with the BungeeCord first which will establish a connection with the database server.
|
||||
wait-on-proxy: '&6Sending request... (Do not forget to follow the BungeeCord setup guide)'
|
||||
|
||||
# When ProtocolLib is enabled and the plugin is unable to continue handling a login request after a requested premium
|
||||
# authentication. In this state the client expects a success packet with a encrypted connection or disconnect packet.
|
||||
# So we kick the player, if we cannot encrypt the connection. In other situation (example: premium name check),
|
||||
# the player will be just authenticated as cracked
|
||||
error-kick: '&4Error occurred'
|
||||
|
||||
# The server sends a verify token within the premium authentication request. If this doesn't match on response,
|
||||
# it could be another client sending malicious packets
|
||||
invalid-verify-token: '&4Invalid token'
|
||||
|
||||
# The client sent no request join server request to the mojang servers which would proof that it's owner of that
|
||||
# account. Only modified clients would do this.
|
||||
invalid-session: '&4Invalid session'
|
||||
|
||||
# The client sent a malicious packet without a login request packet
|
||||
invalid-requst: '&4Invalid request'
|
||||
|
||||
# Message if the Bukkit isn't fully started to inject the packets
|
||||
not-started: '&cServer is not fully started yet. Please retry'
|
||||
|
||||
# Warning message if a user invoked /premium command
|
||||
premium-warning: '&c&lWARNING: &6This command should &lonly&6 be invoked if you are the owner of this paid Minecraft account
|
||||
Type &a/premium&6 again to confirm'
|
||||
|
||||
# ========= Bungee/Waterfall only ================================
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class RateLimiterTest {
|
||||
|
||||
private static final long THRESHOLD_MILLI = 10;
|
||||
|
||||
/**
|
||||
* Always expired
|
||||
*/
|
||||
@Test
|
||||
public void allowExpire() throws InterruptedException {
|
||||
int size = 3;
|
||||
|
||||
// run twice the size to fill it first and then test it
|
||||
RateLimiter rateLimiter = new RateLimiter(size, 0);
|
||||
for (int i = 0; i < size; i++) {
|
||||
assertTrue("Filling up", rateLimiter.tryAcquire());
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
Thread.sleep(1);
|
||||
assertTrue("Should be expired", rateLimiter.tryAcquire());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Too many requests
|
||||
*/
|
||||
@Test
|
||||
public void shoudBlock() {
|
||||
int size = 3;
|
||||
|
||||
// fill the size
|
||||
RateLimiter rateLimiter = new RateLimiter(size, TimeUnit.SECONDS.toMillis(30));
|
||||
for (int i = 0; i < size; i++) {
|
||||
assertTrue("Filling up", rateLimiter.tryAcquire());
|
||||
}
|
||||
|
||||
assertFalse("Should be full and no entry should be expired", rateLimiter.tryAcquire());
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocked attempts shouldn't replace existing ones.
|
||||
*/
|
||||
@Test
|
||||
public void blockedNotAdded() throws InterruptedException {
|
||||
// fill the size - 100ms should be reasonable high
|
||||
RateLimiter rateLimiter = new RateLimiter(1, 100);
|
||||
assertTrue("Filling up", rateLimiter.tryAcquire());
|
||||
|
||||
Thread.sleep(50);
|
||||
|
||||
// still is full - should fail
|
||||
assertFalse("Expired too early", rateLimiter.tryAcquire());
|
||||
|
||||
// wait the remaining time and add a threshold, because
|
||||
Thread.sleep(50 + THRESHOLD_MILLI);
|
||||
assertTrue("Request not released", rateLimiter.tryAcquire());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user