Compare commits

...

40 Commits
1.8 ... 1.9

Author SHA1 Message Date
games647
87ca00d75d [SwitchMode] Kick the player only if the player is unknown to us 2016-09-21 09:16:19 +02:00
games647
62ffb1a904 [Bukkit] Fix adding to premium whitelist 2016-09-20 13:55:03 +02:00
games647
5075a71843 A few code styling things 2016-09-20 13:32:06 +02:00
games647
da266c7e91 Fix loading of settings 2016-09-19 17:59:45 +02:00
games647
acab4766b1 Remove database migration logging 2016-09-19 15:59:57 +02:00
games647
bef90d11cd Deploy only the universal jar to the target folder 2016-09-18 11:40:21 +02:00
games647
a02acd2d63 Remove the nasty UltraAuth fakeplayer workaround 2016-09-18 10:38:05 +02:00
games647
ca42a7c19e Refactor more code for more Java 8 and Guava usage 2016-09-17 15:19:07 +02:00
games647
b533197f05 Fix config loading in BungeeCord 2016-09-17 15:19:07 +02:00
Maxetto
c94711f315 Fix verb (#79) 2016-09-17 08:02:33 +02:00
games647
ee7af80bf0 Fix travis 2016-09-16 17:41:23 +02:00
games647
17c2099bf1 Make use of the awesome Java 8 features 2016-09-16 17:40:42 +02:00
games647
31d6b67381 Try to upgrade to Java 8. I hope enough people are using it. 2016-09-16 16:31:47 +02:00
games647
4b423c9ccb Update ProtocolSupport and use a maven repository for it now 2016-09-16 10:45:05 +02:00
games647
4292e9aaa0 Less deprecated warnings + Clean up 2016-09-15 11:10:52 +02:00
games647
07d0aededa Fix loading with unloaded configuration values 2016-09-15 10:33:17 +02:00
games647
218bc50c96 Drop support for LoginSecurity 1.X since 2.X seems to be stable 2016-09-14 17:44:32 +02:00
games647
a3b2e33aad Switch to vik1395 repository for BungeeAuth 2016-09-13 09:53:46 +02:00
games647
76f5ba7ed1 Refactor a lot of code + Add Guava v10 as shared library 2016-09-11 21:26:03 +02:00
games647
2cd50d23ad Revert converting auth hooks to the new format
-> backwards compatibility
2016-09-11 20:07:21 +02:00
games647
9f5f61f1c2 Do the same for the password generator 2016-09-11 19:57:27 +02:00
games647
3e9c8e3a7e More shared project code for less errors and less duplication 2016-09-11 18:59:42 +02:00
games647
8e5da01be0 Added configuration to disable auto logins for 2Factor authentication
(Fixes #65)
2016-09-09 16:52:44 +02:00
games647
5022c9aa7b Add cracked whitelist (Fixes #42)
(switch-mode -> switching to online-mode from offlinemode)
2016-09-09 16:40:24 +02:00
games647
ad1ab22586 Test another locale sqlite fix 2016-09-08 11:48:13 +02:00
games647
99ef5ce726 Fix correct cracked permission for bukkit 2016-09-08 10:06:43 +02:00
games647
9b7634a9f3 Fix LogIt repository 2016-09-04 16:35:57 +02:00
games647
115fc2e7ba A try to fix SQLite timestamp parsing 2016-09-04 12:14:28 +02:00
games647
b660951e1e Fix compatibility with older ProtocolLib versions (for 1.7)
because of the missing getMethodAcccessorOrNull method
2016-09-03 10:21:42 +02:00
games647
e495f70ccd Fix logging exceptions on encryption enabling 2016-09-01 20:09:34 +02:00
games647
b35d67b5c0 Add missing add-premium-other message 2016-09-01 19:41:58 +02:00
games647
58ac73a5a9 Fix update username in FastLogin database after nameChange (Fixes #67) 2016-08-31 13:29:06 +02:00
games647
ebe768f7a2 Fix ProtocolSupport autoRegister 2016-08-30 12:27:02 +02:00
games647
d20db79f46 Add second attemp login -> cracked (Fixes #51) 2016-08-29 17:38:46 +02:00
games647
c28d889c1b Remove complex nameChange convert since we now allow duplicate uuids 2016-08-26 13:32:28 +02:00
games647
ad60397851 Added auto login importers 2016-08-25 17:45:09 +02:00
games647
88fdeff3f1 Update documentation 2016-08-25 17:44:51 +02:00
games647
558ee1c92c Fix console support for cracked/premium commands (Fixes# 62) 2016-08-20 17:38:47 +02:00
games647
3e84ebd787 [BungeeAuth] Do not login the player if it's already logged in using
sessions
2016-08-19 22:00:07 +02:00
games647
36d7564c3a Fix race condition when waiting for bukkit message while
bungee redirects player
2016-08-19 21:07:29 +02:00
77 changed files with 1712 additions and 2784 deletions

View File

@@ -8,8 +8,5 @@ language: java
script: mvn compile test
# We run on 7+
jdk:
- openjdk7
- oraclejdk7
- oraclejdk8
# We run on 8
jdk: [oraclejdk8]

View File

@@ -1,7 +1,30 @@
######1.9
* Added second attempt login -> cracked login
* Added cracked whitelist (switch-mode -> switching to online-mode from offlinemode)
* Added configuration to disable auto logins for 2Factor authentication
* Added missing add-premium-other message
* Upgrade to Java 8 -> Minimize file size
* Refactored/Cleaned up a lot of code
* [API] Deprecated platform specific authplugin. Please use AuthPlugin< platform specific player type >
* [API] Deprecated bukkit's password generator. Please use PasswordGenerator< platform specific player type >
* Fix ProtocolSupport autoRegister
* Fix update username in FastLogin database after nameChange
* Fix logging exceptions on encryption enabling
* Fix compatibility with older ProtocolLib versions (for 1.7) because of the missing getMethodAcccessorOrNull method
* Fix correct cracked permission for bukkit
* A try to fix SQLite timestamp parsing
* Drop support for LoginSecurity 1.X since 2.X seems to be stable
* Remove the nasty UltraAuth fakeplayer workaround by using a new api method. You should UltraAuth if you have it
######1.8
* Added autoIn importer
* Added BFA importer
* Added ElDziAuth importer
* Fix third-party not premium player detection
* Fix ProtocolSupport BungeeCord
* Fix duplicate logins for BungeeAuth users
######1.7.1

View File

@@ -22,6 +22,7 @@ So they don't need to enter passwords. This is also called auto login (auto-logi
* No client modifications needed
* Good performance by using async non blocking operations
* Locale messages
* Import the database from similar plugins
* Free
* Open source
@@ -30,10 +31,14 @@ So they don't need to enter passwords. This is also called auto login (auto-logi
###Commands:
* /premium [player] Label the invoker or the argument as paid account
* /cracked [player] Label the invoker or the argument as cracked account
* /importdb <autoIn/bpa/eldzi> <mysql/sqlite> [host:port] [database] [username] [password] - Imports the database from another plugin
###Permissions:
* fastlogin.bukkit.command.premium
* fastlogin.bukkit.command.cracked
* fastlogin.command.premium.other
* fastlogin.command.cracked.other
* fastlogin.command.import
###Requirements:
* Plugin: [ProtocolLib](http://www.spigotmc.org/resources/protocollib.1997/) or [ProtocolSupport](http://www.spigotmc.org/resources/protocolsupport.7201/)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.7.1</version>
<version>1.9</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -46,11 +46,6 @@
<url>http://repo.luricos.de/bukkit-plugins/</url>
</repository>
<repository>
<id>logit-only-repo</id>
<url>http://ci.ac3-servers.eu/job/LogIt-Classic/2/maven-repository/repository/</url>
</repository>
<!--Github automatic maven builds-->
<repository>
<id>jitpack.io</id>
@@ -78,17 +73,15 @@
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>4.0.1</version>
<optional>true</optional>
<version>3.6.5</version>
</dependency>
<dependency>
<groupId>protcolsupport</groupId>
<artifactId>ProtocolSupport</artifactId>
<version>Build-337</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/ProtocolSupport b337.jar</systemPath>
</dependency>
<dependency>
<groupId>com.github.ProtocolSupport</groupId>
<artifactId>ProtocolSupport</artifactId>
<!--4.25.dev-->
<version>5554413b51</version>
</dependency>
<!--Login Plugins-->
<dependency>
@@ -104,24 +97,11 @@
</exclusions>
</dependency>
<dependency>
<!--Oringal owner is lenis, but maven always uses the newest version-->
<groupId>com.github.lenis0012</groupId>
<artifactId>LoginSecurity-2</artifactId>
<!--Old version 2.0 -->
<version>-9c09e73b7f-1</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.lenis0012.bukkit</groupId>
<artifactId>loginsecurity</artifactId>
<version>2.1.3-SNAPSHOT</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>*</groupId>
@@ -130,22 +110,25 @@
</exclusions>
</dependency>
<dependency>
<groupId>io.github.lucaseasedup.logit</groupId>
<artifactId>LogIt</artifactId>
<version>SNAPSHOT</version>
<dependency>
<groupId>com.github.games647</groupId>
<artifactId>LogIt</artifactId>
<version>9e3581db27</version>
<optional>true</optional>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependency>
<dependency>
<groupId>com.github.RoyalDev</groupId>
<artifactId>RoyalAuth</artifactId>
<version>-e21354a9b7-1</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>*</groupId>
@@ -193,7 +176,7 @@
<version>2.0.2</version>
<optional>true</optional>
<scope>system</scope>
<systemPath>${project.basedir}/lib/UltraAuth v2.0.2.jar</systemPath>
<systemPath>${project.basedir}/lib/UltraAuth v2.1.2.jar</systemPath>
</dependency>
</dependencies>
</project>

View File

@@ -1,22 +1,27 @@
package com.github.games647.fastlogin.bukkit;
import com.github.games647.fastlogin.core.FastLoginCore;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.MojangApiConnector;
import com.google.common.base.Charsets;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.File;
import java.io.InputStreamReader;
import java.util.List;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Logger;
import org.bukkit.ChatColor;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
public class BukkitCore extends FastLoginCore {
public class BukkitCore extends FastLoginCore<Player> {
private final FastLoginBukkit plugin;
public BukkitCore(FastLoginBukkit plugin) {
super(plugin.getConfig().getValues(false));
this.plugin = plugin;
}
@@ -49,25 +54,19 @@ public class BukkitCore extends FastLoginCore {
InputStreamReader defaultReader = new InputStreamReader(plugin.getResource("messages.yml"), Charsets.UTF_8);
YamlConfiguration defaults = YamlConfiguration.loadConfiguration(defaultReader);
for (String key : defaults.getKeys(false)) {
String message = ChatColor.translateAlternateColorCodes('&', defaults.getString(key));
messageConfig.setDefaults(defaults);
messageConfig.getKeys(false).forEach((key) -> {
String message = ChatColor.translateAlternateColorCodes('&', messageConfig.getString(key));
if (!message.isEmpty()) {
localeMessages.put(key, message);
}
}
for (String key : messageConfig.getKeys(false)) {
String message = ChatColor.translateAlternateColorCodes('&', messageConfig.getString(key));
if (message.isEmpty()) {
localeMessages.remove(key);
} else {
localeMessages.put(key, message);
}
}
});
}
@Override
public void loadConfig() {
plugin.saveDefaultConfig();
public MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests) {
return new MojangApiBukkit(logger, addresses, requests);
}
}

View File

@@ -1,9 +1,7 @@
package com.github.games647.fastlogin.bukkit;
import com.github.games647.fastlogin.core.LoginSession;
import com.github.games647.fastlogin.core.PlayerProfile;
import java.util.UUID;
import com.github.games647.fastlogin.core.shared.LoginSession;
import org.apache.commons.lang.ArrayUtils;
@@ -17,7 +15,6 @@ public class BukkitLoginSession extends LoginSession {
private final String serverId;
private final byte[] verifyToken;
private UUID uuid;
private boolean verified;
private String encodedSkinData;
@@ -93,24 +90,6 @@ public class BukkitLoginSession extends LoginSession {
this.verified = verified;
}
/**
* 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;
}
/**
* Get whether the player has a premium (paid account) account and valid session
*

View File

@@ -1,12 +0,0 @@
package com.github.games647.fastlogin.bukkit;
import org.apache.commons.lang.RandomStringUtils;
import org.bukkit.entity.Player;
public class DefaultPasswordGenerator implements PasswordGenerator {
@Override
public String getRandomPassword(Player player) {
return RandomStringUtils.random(8, true, true);
}
}

View File

@@ -10,6 +10,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.stream.Stream;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@@ -48,9 +49,7 @@ public class EncryptionUtil {
private static byte[] digestOperation(String algo, byte[]... content) {
try {
MessageDigest messagedigest = MessageDigest.getInstance(algo);
for (byte[] data : content) {
messagedigest.update(data);
}
Stream.of(content).forEach(messagedigest::update);
return messagedigest.digest();
} catch (NoSuchAlgorithmException nosuchalgorithmexception) {

View File

@@ -4,6 +4,7 @@ import com.avaje.ebeaninternal.api.ClassUtil;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.ProtocolLibrary;
import com.github.games647.fastlogin.bukkit.commands.CrackedCommand;
import com.github.games647.fastlogin.bukkit.commands.ImportCommand;
import com.github.games647.fastlogin.bukkit.commands.PremiumCommand;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
import com.github.games647.fastlogin.bukkit.listener.BukkitJoinListener;
@@ -13,18 +14,16 @@ import com.github.games647.fastlogin.bukkit.listener.protocollib.LoginSkinApplyL
import com.github.games647.fastlogin.bukkit.listener.protocollib.StartPacketListener;
import com.github.games647.fastlogin.bukkit.listener.protocolsupport.ProtocolSupportListener;
import com.github.games647.fastlogin.bukkit.tasks.DelayedAuthHook;
import com.github.games647.fastlogin.core.FastLoginCore;
import com.google.common.cache.CacheLoader;
import com.google.common.collect.Sets;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.google.common.collect.Iterables;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import java.security.KeyPair;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
@@ -39,36 +38,25 @@ public class FastLoginBukkit extends JavaPlugin {
private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
private boolean bungeeCord;
private final FastLoginCore core = new BukkitCore(this);
private BukkitCore core;
private boolean serverStarted;
private final Set<UUID> pendingConfirms = Sets.newHashSet();
//this map is thread-safe for async access (Packet Listener)
//SafeCacheBuilder is used in order to be version independent
private final ConcurrentMap<String, BukkitLoginSession> session = buildCache(1, -1);
//1 minutes should be enough as a timeout for bad internet connection (Server, Client and Mojang)
private final ConcurrentMap<String, BukkitLoginSession> session = FastLoginCore.buildCache(1, -1);
private BukkitAuthPlugin authPlugin;
private PasswordGenerator passwordGenerator = new DefaultPasswordGenerator();
@Override
public void onEnable() {
core.loadConfig();
saveDefaultConfig();
core = new BukkitCore(this);
core.loadMessages();
List<String> ipAddresses = getConfig().getStringList("ip-addresses");
int requestLimit = getConfig().getInt("mojang-request-limit");
MojangApiBukkit mojangApi = new MojangApiBukkit(buildCache(10, -1), getLogger(), ipAddresses, requestLimit);
core.setMojangApiConnector(mojangApi);
core.setApiConnector();
try {
if (ClassUtil.isPresent("org.spigotmc.SpigotConfig")) {
bungeeCord = Class.forName("org.spigotmc.SpigotConfig").getDeclaredField("bungee").getBoolean(null);
}
} catch (Exception | NoSuchMethodError ex) {
getLogger().warning("Cannot check bungeecord support. You use a non-spigot build");
ex.printStackTrace();
} catch (Exception ex) {
getLogger().log(Level.WARNING, "Cannot check bungeecord support. You use a non-spigot build", ex);
}
if (getServer().getOnlineMode()) {
@@ -80,21 +68,13 @@ public class FastLoginBukkit extends JavaPlugin {
if (bungeeCord) {
setServerStarted();
//check for incoming messages from the bungeecord version of this plugin
getServer().getMessenger().registerIncomingPluginChannel(this, getName(), new BungeeCordListener(this));
getServer().getMessenger().registerOutgoingPluginChannel(this, getName());
//register listeners on success
} else {
String driver = getConfig().getString("driver");
String host = getConfig().getString("host", "");
int port = getConfig().getInt("port", 3306);
String database = getConfig().getString("database");
String username = getConfig().getString("username", "");
String password = getConfig().getString("password", "");
if (!core.setupDatabase(driver, host, port, database, username, password)) {
if (!core.setupDatabase()) {
setEnabled(false);
return;
}
@@ -125,11 +105,11 @@ public class FastLoginBukkit extends JavaPlugin {
//register commands using a unique name
getCommand("premium").setExecutor(new PremiumCommand(this));
getCommand("cracked").setExecutor(new CrackedCommand(this));
getCommand("import-auth").setExecutor(new ImportCommand(core));
}
@Override
public void onDisable() {
//clean up
session.clear();
if (core != null) {
@@ -137,21 +117,30 @@ public class FastLoginBukkit extends JavaPlugin {
}
//remove old blacklists
for (Player player : getServer().getOnlinePlayers()) {
player.removeMetadata(getName(), this);
}
getServer().getOnlinePlayers().forEach(player -> player.removeMetadata(getName(), this));
}
public FastLoginCore getCore() {
public BukkitCore getCore() {
return core;
}
public String generateStringPassword(Player player) {
return passwordGenerator.getRandomPassword(player);
public void sendBungeeActivateMessage(CommandSender sender, String target, boolean activate) {
if (sender instanceof Player) {
notifiyBungeeCord((Player) sender, target, activate);
} else {
Player firstPlayer = Iterables.getFirst(getServer().getOnlinePlayers(), null);
if (firstPlayer == null) {
getLogger().info("No player online to send a plugin message to the proxy");
return;
}
notifiyBungeeCord(firstPlayer, target, activate);
}
}
@Deprecated
public void setPasswordGenerator(PasswordGenerator passwordGenerator) {
this.passwordGenerator = passwordGenerator;
core.setPasswordGenerator(passwordGenerator);
}
/**
@@ -179,20 +168,14 @@ public class FastLoginBukkit extends JavaPlugin {
*
* @return interface to any supported auth plugin
*/
@Deprecated
public BukkitAuthPlugin getAuthPlugin() {
if (authPlugin == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
getLogger().log(Level.SEVERE, null, ex);
}
}
return authPlugin;
return (BukkitAuthPlugin) core.getAuthPluginHook();
}
@Deprecated
public void setAuthPluginHook(BukkitAuthPlugin authPlugin) {
this.authPlugin = authPlugin;
core.setAuthPluginHook(authPlugin);
}
public boolean isBungeeCord() {
@@ -209,32 +192,21 @@ public class FastLoginBukkit extends JavaPlugin {
return serverStarted;
}
public Set<UUID> getPendingConfirms() {
return pendingConfirms;
}
public void setServerStarted() {
if (!this.serverStarted) {
this.serverStarted = true;
}
}
private <K, V> ConcurrentMap<K, V> buildCache(int minutes, int maxSize) {
CompatibleCacheBuilder<Object, Object> builder = CompatibleCacheBuilder.newBuilder();
if (minutes > 0) {
builder.expireAfterWrite(minutes, TimeUnit.MINUTES);
private void notifiyBungeeCord(Player sender, String target, boolean activate) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
if (activate) {
dataOutput.writeUTF("ON");
} else {
dataOutput.writeUTF("OFF");
}
if (maxSize > 0) {
builder.maximumSize(maxSize);
}
return builder.build(new CacheLoader<K, V>() {
@Override
public V load(K key) throws Exception {
throw new UnsupportedOperationException("Not supported yet.");
}
});
dataOutput.writeUTF(target);
sender.sendPluginMessage(this, getName(), dataOutput.toByteArray());
}
}

View File

@@ -1,14 +1,13 @@
package com.github.games647.fastlogin.bukkit;
import com.github.games647.fastlogin.core.FastLoginCore;
import com.github.games647.fastlogin.core.MojangApiConnector;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.shared.MojangApiConnector;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -21,17 +20,12 @@ public class MojangApiBukkit extends MojangApiConnector {
//mojang api check to prove a player is logged in minecraft and made a join server request
private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?";
public MojangApiBukkit(ConcurrentMap<Object, Object> requests, Logger logger, List<String> localAddresses
, int rateLimit) {
super(requests, logger, localAddresses, rateLimit);
public MojangApiBukkit(Logger logger, List<String> localAddresses, int rateLimit) {
super(logger, localAddresses, rateLimit);
}
@Override
public boolean hasJoinedServer(Object session, String serverId) {
if (!(session instanceof BukkitLoginSession)) {
return false;
}
public boolean hasJoinedServer(LoginSession session, String serverId) {
BukkitLoginSession playerSession = (BukkitLoginSession) session;
try {
String url = HAS_JOINED_URL + "username=" + playerSession.getUsername() + "&serverId=" + serverId;
@@ -68,7 +62,7 @@ public class MojangApiBukkit extends MojangApiConnector {
}
@Override
protected UUID getUUIDFromJson(String json) {
protected String getUUIDFromJson(String json) {
boolean isArray = json.startsWith("[");
JSONObject mojangPlayer;
@@ -80,10 +74,10 @@ public class MojangApiBukkit extends MojangApiConnector {
}
String uuid = (String) mojangPlayer.get("id");
if (uuid == null || uuid.equals("null")) {
if ("null".equals(uuid)) {
return null;
}
return FastLoginCore.parseId(uuid);
return uuid;
}
}

View File

@@ -2,7 +2,13 @@ package com.github.games647.fastlogin.bukkit;
import org.bukkit.entity.Player;
public interface PasswordGenerator {
/**
*
* @deprecated please use com.github.games647.fastlogin.core.hooks.PasswordGenerator<org.bukkit.entity.Player>
*/
@Deprecated
public interface PasswordGenerator extends com.github.games647.fastlogin.core.hooks.PasswordGenerator<Player> {
@Override
String getRandomPassword(Player player);
}

View File

@@ -2,8 +2,6 @@ package com.github.games647.fastlogin.bukkit.commands;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
@@ -13,7 +11,7 @@ import org.bukkit.entity.Player;
public class CrackedCommand implements CommandExecutor {
protected final FastLoginBukkit plugin;
private final FastLoginBukkit plugin;
public CrackedCommand(FastLoginBukkit plugin) {
this.plugin = plugin;
@@ -29,23 +27,21 @@ public class CrackedCommand implements CommandExecutor {
}
if (plugin.isBungeeCord()) {
notifiyBungeeCord(sender, sender.getName());
plugin.sendBungeeActivateMessage(sender, sender.getName(), false);
String message = plugin.getCore().getMessage("wait-on-proxy");
if (message != null) {
sender.sendMessage(message);
}
} else {
//todo: load async if
final PlayerProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
if (profile.isPremium()) {
sender.sendMessage(plugin.getCore().getMessage("remove-premium"));
profile.setPremium(false);
profile.setUuid(null);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
plugin.getCore().getStorage().save(profile);
}
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
plugin.getCore().getStorage().save(profile);
});
} else {
sender.sendMessage(plugin.getCore().getMessage("not-premium"));
@@ -67,52 +63,29 @@ public class CrackedCommand implements CommandExecutor {
}
if (plugin.isBungeeCord()) {
notifiyBungeeCord(sender, args[0]);
plugin.sendBungeeActivateMessage(sender, args[0], false);
String message = plugin.getCore().getMessage("wait-on-proxy");
if (message != null) {
sender.sendMessage(message);
}
} else {
//todo: load async
final PlayerProfile profile = plugin.getCore().getStorage().loadProfile(args[0]);
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(args[0]);
if (profile == null) {
sender.sendMessage(plugin.getCore().getMessage("player-unknown"));
sender.sendMessage("Error occured");
return;
}
if (profile.isPremium()) {
//existing player is already cracked
if (profile.getUserId() != -1 && !profile.isPremium()) {
sender.sendMessage(plugin.getCore().getMessage("not-premium-other"));
} else {
sender.sendMessage(plugin.getCore().getMessage("remove-premium"));
profile.setPremium(false);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
plugin.getCore().getStorage().save(profile);
}
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
plugin.getCore().getStorage().save(profile);
});
} else {
sender.sendMessage(plugin.getCore().getMessage("not-premium-other"));
}
}
}
private void notifiyBungeeCord(CommandSender sender, String target) {
if (sender instanceof Player) {
notifiyBungeeCord((Player) sender, target);
} else {
plugin.getLogger().info("No player online to send a plugin message to the proxy");
//todo: add console support
// Player firstPlayer = Iterables.getFirst(Bukkit.getOnlinePlayers(), null);
// notifiyBungeeCord(firstPlayer, target);
}
}
private void notifiyBungeeCord(Player sender, String target) {
if (plugin.isBungeeCord()) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
dataOutput.writeUTF("OFF");
dataOutput.writeUTF(target);
sender.sendPluginMessage(plugin, plugin.getName(), dataOutput.toByteArray());
}
}
}

View File

@@ -0,0 +1,83 @@
package com.github.games647.fastlogin.bukkit.commands;
import com.github.games647.fastlogin.bukkit.BukkitCore;
import com.github.games647.fastlogin.core.AuthStorage;
import com.github.games647.fastlogin.core.importer.ImportPlugin;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
public class ImportCommand implements CommandExecutor {
private final BukkitCore core;
public ImportCommand(BukkitCore core) {
this.core = core;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length < 2) {
sender.sendMessage(ChatColor.DARK_RED + "You need to specify the import plugin and database type");
return true;
}
ImportPlugin importPlugin;
switch (args[0].toLowerCase()) {
case "autoin":
importPlugin = ImportPlugin.AUTO_IN;
break;
case "bpa":
importPlugin = ImportPlugin.BPA;
break;
case "eldzi":
importPlugin = ImportPlugin.ELDZI;
break;
default:
sender.sendMessage(ChatColor.DARK_RED + "Unknown auto login plugin");
return true;
}
boolean sqlite;
switch (args[1].toLowerCase()) {
case "sqlite":
sqlite = true;
break;
case "mysql":
sqlite = false;
break;
default:
sender.sendMessage(ChatColor.DARK_RED + "Unknown storage type to import from. Either SQLite or MySQL");
return true;
}
String host = "";
String database = "";
String username = "";
String password = "";
if (!sqlite) {
if (args.length <= 5) {
sender.sendMessage(ChatColor.DARK_RED + "If importing from MySQL, you need to specify host database "
+ "and username passowrd too");
return true;
}
host = args[2];
database = args[3];
username = args[4];
password = args[5];
}
AuthStorage storage = core.getStorage();
boolean success = core.importDatabase(importPlugin, true, storage, host, database, username, password);
if (success) {
sender.sendMessage(ChatColor.DARK_GREEN + "Successful imported the data");
} else {
sender.sendMessage(ChatColor.DARK_RED + "Failed to import the data. Check out the logs");
}
return true;
}
}

View File

@@ -2,8 +2,6 @@ package com.github.games647.fastlogin.bukkit.commands;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import java.util.UUID;
@@ -20,7 +18,7 @@ import org.bukkit.entity.Player;
*/
public class PremiumCommand implements CommandExecutor {
protected final FastLoginBukkit plugin;
private final FastLoginBukkit plugin;
public PremiumCommand(FastLoginBukkit plugin) {
this.plugin = plugin;
@@ -36,32 +34,30 @@ public class PremiumCommand implements CommandExecutor {
}
if (plugin.isBungeeCord()) {
notifiyBungeeCord(sender, sender.getName());
plugin.sendBungeeActivateMessage(sender, sender.getName(), true);
String message = plugin.getCore().getMessage("wait-on-proxy");
if (message != null) {
sender.sendMessage(message);
}
} else {
UUID id = ((Player) sender).getUniqueId();
if (plugin.getConfig().getBoolean("premium-warning") && !plugin.getPendingConfirms().contains(id)) {
if (plugin.getConfig().getBoolean("premium-warning")
&& !plugin.getCore().getPendingConfirms().contains(id)) {
sender.sendMessage(plugin.getCore().getMessage("premium-warning"));
plugin.getPendingConfirms().add(id);
plugin.getCore().getPendingConfirms().add(id);
return true;
}
plugin.getPendingConfirms().remove(id);
plugin.getCore().getPendingConfirms().remove(id);
//todo: load async
final PlayerProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
if (profile.isPremium()) {
sender.sendMessage(plugin.getCore().getMessage("already-exists"));
} else {
//todo: resolve uuid
profile.setPremium(true);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
plugin.getCore().getStorage().save(profile);
}
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
plugin.getCore().getStorage().save(profile);
});
sender.sendMessage(plugin.getCore().getMessage("add-premium"));
@@ -83,14 +79,14 @@ public class PremiumCommand implements CommandExecutor {
}
if (plugin.isBungeeCord()) {
notifiyBungeeCord(sender, args[0]);
plugin.sendBungeeActivateMessage(sender, args[0], true);
String message = plugin.getCore().getMessage("wait-on-proxy");
if (message != null) {
sender.sendMessage(message);
}
} else {
//todo: load async
final PlayerProfile profile = plugin.getCore().getStorage().loadProfile(args[0]);
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(args[0]);
if (profile == null) {
sender.sendMessage(plugin.getCore().getMessage("player-unknown"));
return;
@@ -101,36 +97,12 @@ public class PremiumCommand implements CommandExecutor {
} else {
//todo: resolve uuid
profile.setPremium(true);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
plugin.getCore().getStorage().save(profile);
}
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
plugin.getCore().getStorage().save(profile);
});
sender.sendMessage(plugin.getCore().getMessage("add-premium-other"));
}
}
}
private void notifiyBungeeCord(CommandSender sender, String target) {
if (sender instanceof Player) {
notifiyBungeeCord((Player) sender, target);
} else {
plugin.getLogger().info("No player online to send a plugin message to the proxy");
//todo: add console support
// Player firstPlayer = Iterables.getFirst(Bukkit.getOnlinePlayers(), null);
// notifiyBungeeCord(firstPlayer, target);
}
}
private void notifiyBungeeCord(Player sender, String target) {
if (plugin.isBungeeCord()) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
dataOutput.writeUTF("ON");
dataOutput.writeUTF(target);
sender.sendPluginMessage(plugin, plugin.getName(), dataOutput.toByteArray());
}
}
}

View File

@@ -1,6 +1,7 @@
package com.github.games647.fastlogin.bukkit.hooks;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import fr.xephi.authme.api.API;
import fr.xephi.authme.api.NewAPI;
@@ -14,7 +15,7 @@ import org.bukkit.entity.Player;
* Bukkit: http://dev.bukkit.org/bukkit-plugins/authme-reloaded/
* Spigot: https://www.spigotmc.org/resources/authme-reloaded.6269/
*/
public class AuthMeHook implements BukkitAuthPlugin {
public class AuthMeHook implements AuthPlugin<Player> {
private final boolean isNewAPIAvailable;
@@ -23,6 +24,7 @@ public class AuthMeHook implements BukkitAuthPlugin {
}
@Override
@SuppressWarnings("deprecation")
public boolean forceLogin(Player player) {
//skips registration and login
if (isNewAPIAvailable) {
@@ -35,6 +37,7 @@ public class AuthMeHook implements BukkitAuthPlugin {
}
@Override
@SuppressWarnings("deprecation")
public boolean isRegistered(String playerName) throws Exception {
if (isNewAPIAvailable) {
return NewAPI.getInstance().isRegistered(playerName);
@@ -44,6 +47,7 @@ public class AuthMeHook implements BukkitAuthPlugin {
}
@Override
@SuppressWarnings("deprecation")
public boolean forceRegister(Player player, String password) {
if (isNewAPIAvailable) {
NewAPI.getInstance().forceRegister(player, password);

View File

@@ -1,60 +1,21 @@
package com.github.games647.fastlogin.bukkit.hooks;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import org.bukkit.entity.Player;
/**
* Represents a supporting authentication plugin in Bukkit/Spigot/... servers
* @deprecated please use com.github.games647.fastlogin.core.hooks.AuthPlugin<org.bukkit.entity.Player>
*/
public interface BukkitAuthPlugin {
@Deprecated
public interface BukkitAuthPlugin extends AuthPlugin<Player> {
/**
* 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
*/
@Override
boolean forceLogin(Player player);
/**
* 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
*/
@Override
boolean isRegistered(String playerName) throws Exception;
/**
* 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
*/
@Override
boolean forceRegister(Player player, String password);
}

View File

@@ -1,12 +1,13 @@
package com.github.games647.fastlogin.bukkit.hooks;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import de.st_ddt.crazylogin.CrazyLogin;
import de.st_ddt.crazylogin.data.LoginPlayerData;
import de.st_ddt.crazylogin.databases.CrazyLoginDataDatabase;
import de.st_ddt.crazylogin.listener.PlayerListener;
import de.st_ddt.crazylogin.metadata.Authenticated;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
@@ -22,47 +23,43 @@ import org.bukkit.entity.Player;
*
* Bukkit: http://dev.bukkit.org/server-mods/crazylogin/
*/
public class CrazyLoginHook implements BukkitAuthPlugin {
public class CrazyLoginHook implements AuthPlugin<Player> {
protected final CrazyLogin crazyLoginPlugin = CrazyLogin.getPlugin();
private final CrazyLogin crazyLoginPlugin = CrazyLogin.getPlugin();
private final PlayerListener playerListener = getListener();
@Override
public boolean forceLogin(final Player player) {
public boolean forceLogin(Player player) {
//not thread-safe operation
Future<LoginPlayerData> future = Bukkit.getScheduler().callSyncMethod(crazyLoginPlugin
, new Callable<LoginPlayerData>() {
@Override
public LoginPlayerData call() throws Exception {
LoginPlayerData playerData = crazyLoginPlugin.getPlayerData(player.getName());
if (playerData != null) {
//mark the account as logged in
playerData.setLoggedIn(true);
Future<LoginPlayerData> future = Bukkit.getScheduler().callSyncMethod(crazyLoginPlugin, () -> {
LoginPlayerData playerData = crazyLoginPlugin.getPlayerData(player);
if (playerData != null) {
//mark the account as logged in
playerData.setLoggedIn(true);
String ip = player.getAddress().getAddress().getHostAddress();
String ip = player.getAddress().getAddress().getHostAddress();
//this should be done after login to restore the inventory, unhide players, prevent potential memory leaks...
//from: https://github.com/ST-DDT/CrazyLogin/blob/master/src/main/java/de/st_ddt/crazylogin/CrazyLogin.java#L1948
playerData.resetLoginFails();
player.setFireTicks(0);
playerData.resetLoginFails();
player.setFireTicks(0);
if (playerListener != null) {
playerListener.removeMovementBlocker(player);
playerListener.disableHidenInventory(player);
playerListener.disableSaveLogin(player);
playerListener.unhidePlayer(player);
}
//loginFailuresPerIP.remove(IP);
//illegalCommandUsesPerIP.remove(IP);
//tempBans.remove(IP);
playerData.addIP(ip);
player.setMetadata("Authenticated", new Authenticated(crazyLoginPlugin, player));
crazyLoginPlugin.unregisterDynamicHooks();
return playerData;
if (playerListener != null) {
playerListener.removeMovementBlocker(player);
playerListener.disableHidenInventory(player);
playerListener.disableSaveLogin(player);
playerListener.unhidePlayer(player);
}
return null;
//loginFailuresPerIP.remove(IP);
//illegalCommandUsesPerIP.remove(IP);
//tempBans.remove(IP);
playerData.addIP(ip);
player.setMetadata("Authenticated", new Authenticated(crazyLoginPlugin, player));
crazyLoginPlugin.unregisterDynamicHooks();
return playerData;
}
return null;
});
try {
@@ -86,7 +83,7 @@ public class CrazyLoginHook implements BukkitAuthPlugin {
}
@Override
public boolean forceRegister(final Player player, String password) {
public boolean forceRegister(Player player, String password) {
CrazyLoginDataDatabase crazyDatabase = crazyLoginPlugin.getCrazyDatabase();
//this executes a sql query and accesses only thread safe collections so we can run it async
@@ -108,7 +105,7 @@ public class CrazyLoginHook implements BukkitAuthPlugin {
PlayerListener listener;
try {
listener = (PlayerListener) FieldUtils.readField(crazyLoginPlugin, "playerListener", true);
} catch (Exception ex) {
} catch (IllegalAccessException ex) {
crazyLoginPlugin.getLogger().log(Level.SEVERE, "Failed to get the listener instance for auto login", ex);
listener = null;
}

View File

@@ -1,5 +1,7 @@
package com.github.games647.fastlogin.bukkit.hooks;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import io.github.lucaseasedup.logit.CancelledState;
import io.github.lucaseasedup.logit.LogItCore;
import io.github.lucaseasedup.logit.account.Account;
@@ -13,7 +15,7 @@ import org.bukkit.entity.Player;
* Bukkit: Unknown
* Spigot: Unknown
*/
public class LogItHook implements BukkitAuthPlugin {
public class LogItHook implements AuthPlugin<Player> {
@Override
public boolean forceLogin(Player player) {

View File

@@ -1,21 +1,12 @@
package com.github.games647.fastlogin.bukkit.hooks;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.google.common.base.Charsets;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.lenis0012.bukkit.loginsecurity.LoginSecurity;
import com.lenis0012.bukkit.loginsecurity.session.AuthService;
import com.lenis0012.bukkit.loginsecurity.session.PlayerSession;
import com.lenis0012.bukkit.loginsecurity.session.action.LoginAction;
import com.lenis0012.bukkit.loginsecurity.session.action.RegisterAction;
import com.lenis0012.bukkit.ls.data.DataManager;
import java.net.InetAddress;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@@ -26,105 +17,25 @@ import org.bukkit.entity.Player;
* Bukkit: http://dev.bukkit.org/bukkit-plugins/loginsecurity/
* Spigot: https://www.spigotmc.org/resources/loginsecurity.19362/
*/
public class LoginSecurityHook implements BukkitAuthPlugin {
public class LoginSecurityHook implements AuthPlugin<Player> {
protected final com.lenis0012.bukkit.ls.LoginSecurity securityPlugin;
protected final FastLoginBukkit plugin = (FastLoginBukkit) Bukkit.getPluginManager().getPlugin("FastLogin");
protected final boolean newVersion;
public LoginSecurityHook() {
this.newVersion = ClassUtil.isPresent("com.lenis0012.bukkit.loginsecurity.LoginSecurity", getClass());
if (newVersion) {
this.securityPlugin = null;
} else {
this.securityPlugin = com.lenis0012.bukkit.ls.LoginSecurity.instance;
}
}
private final FastLoginBukkit plugin = (FastLoginBukkit) Bukkit.getPluginManager().getPlugin("FastLogin");
@Override
public boolean forceLogin(Player player) {
if (!newVersion) {
return oldForceLogin(player);
}
PlayerSession session = LoginSecurity.getSessionManager().getPlayerSession(player);
return session.performAction(new LoginAction(AuthService.PLUGIN, plugin)).isSuccess();
}
@Override
public boolean isRegistered(String playerName) throws Exception {
if (!newVersion) {
return oldIsRegistred(playerName);
}
PlayerSession session = LoginSecurity.getSessionManager().getOfflineSession(playerName);
return session.isRegistered();
}
@Override
public boolean forceRegister(Player player, String password) {
if (!newVersion) {
return oldForceRegister(player, password);
}
PlayerSession session = LoginSecurity.getSessionManager().getPlayerSession(player);
return session.performAction(new RegisterAction(AuthService.PLUGIN, plugin, password)).isSuccess();
}
public boolean oldForceLogin(final Player player) {
//Login command of this plugin: (How the plugin logs the player in)
//https://github.com/lenis0012/LoginSecurity-2/blob/master/src/main/java/com/lenis0012/bukkit/ls/commands/LoginCommand.java#L39
//not thread-safe operation
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(securityPlugin, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
String name = player.getName().toLowerCase();
//mark the user as logged in
securityPlugin.authList.remove(name);
//cancel timeout timer
securityPlugin.thread.timeout.remove(name);
//remove effects and restore location
securityPlugin.rehabPlayer(player, name);
return true;
}
});
try {
return future.get();
} catch (InterruptedException | ExecutionException ex) {
securityPlugin.getLogger().log(Level.SEVERE, "Failed to forceLogin", ex);
return false;
}
}
public boolean oldIsRegistred(String playerName) throws Exception {
//https://github.com/lenis0012/LoginSecurity-2/blob/master/src/main/java/com/lenis0012/bukkit/ls/LoginSecurity.java#L296
DataManager dataManager = securityPlugin.data;
//https://github.com/lenis0012/LoginSecurity-2/blob/master/src/main/java/com/lenis0012/bukkit/ls/LoginSecurity.java#L283
UUID offlineUuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + playerName).getBytes(Charsets.UTF_8));
return dataManager.isRegistered(offlineUuid.toString().replace("-", ""));
}
public boolean oldForceRegister(Player player, String password) {
DataManager dataManager = securityPlugin.data;
UUID playerUUID = player.getUniqueId();
String uuidString = playerUUID.toString().replace("-", "");
InetAddress ipAddress = player.getAddress().getAddress();
String passwordHash = securityPlugin.hasher.hash(password);
//this executes a sql query without interacting with other parts so we can run it async.
dataManager.register(uuidString, passwordHash, securityPlugin.hasher.getTypeId(), ipAddress.toString());
String storedPassword = dataManager.getPassword(uuidString);
if (storedPassword != null && storedPassword.equals(passwordHash)) {
//the register method silents any excpetion so check if our entry was saved
return forceLogin(player);
}
return false;
}
}

View File

@@ -1,6 +1,7 @@
package com.github.games647.fastlogin.bukkit.hooks;
import java.util.concurrent.Callable;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
@@ -18,24 +19,20 @@ import org.royaldev.royalauth.RoyalAuth;
*
* Bukkit: http://dev.bukkit.org/bukkit-plugins/royalauth/
*/
public class RoyalAuthHook implements BukkitAuthPlugin {
public class RoyalAuthHook implements AuthPlugin<Player> {
private final RoyalAuth royalAuthPlugin = (RoyalAuth) Bukkit.getPluginManager().getPlugin("RoyalAuth");
@Override
public boolean forceLogin(final Player player) {
//not thread-safe
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(royalAuthPlugin, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
AuthPlayer authPlayer = AuthPlayer.getAuthPlayer(player);
public boolean forceLogin(Player player) {
AuthPlayer authPlayer = AuthPlayer.getAuthPlayer(player);
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(royalAuthPlugin, () -> {
//https://github.com/RoyalDev/RoyalAuth/blob/master/src/main/java/org/royaldev/royalauth/commands/CmdLogin.java#L62
//not thread-safe
authPlayer.login();
//not thread-safe
authPlayer.login();
return authPlayer.isLoggedIn();
}
return authPlayer.isLoggedIn();
});
try {
@@ -58,11 +55,8 @@ public class RoyalAuthHook implements BukkitAuthPlugin {
AuthPlayer authPlayer = AuthPlayer.getAuthPlayer(player);
boolean registerSuccess = authPlayer.setPassword(password, Config.passwordHashType);
if (registerSuccess) {
//login in the player after registration
return forceLogin(player);
}
return false;
//login in the player after registration
return registerSuccess && forceLogin(player);
}
}

View File

@@ -1,6 +1,7 @@
package com.github.games647.fastlogin.bukkit.hooks;
import java.util.concurrent.Callable;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
@@ -19,19 +20,16 @@ import ultraauth.managers.PlayerManager;
* Bukkit: http://dev.bukkit.org/bukkit-plugins/ultraauth-aa/
* Spigot: https://www.spigotmc.org/resources/ultraauth.17044/
*/
public class UltraAuthHook implements BukkitAuthPlugin {
public class UltraAuthHook implements AuthPlugin<Player> {
protected final Plugin ultraAuthPlugin = Main.main;
private final Plugin ultraAuthPlugin = Main.main;
@Override
public boolean forceLogin(final Player player) {
public boolean forceLogin(Player player) {
//not thread-safe
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(ultraAuthPlugin, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
UltraAuthAPI.authenticatedPlayer(player);
return UltraAuthAPI.isAuthenticated(player);
}
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(ultraAuthPlugin, () -> {
UltraAuthAPI.authenticatedPlayer(player);
return UltraAuthAPI.isAuthenticated(player);
});
try {
@@ -44,7 +42,7 @@ public class UltraAuthHook implements BukkitAuthPlugin {
@Override
public boolean isRegistered(String playerName) throws Exception {
return UltraAuthAPI.isRegisterd(new FakePlayer(playerName));
return UltraAuthAPI.isRegisterd(playerName);
}
@Override

View File

@@ -1,9 +1,10 @@
package com.github.games647.fastlogin.bukkit.hooks;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import de.luricos.bukkit.xAuth.xAuth;
import de.luricos.bukkit.xAuth.xAuthPlayer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
@@ -19,27 +20,24 @@ import org.bukkit.entity.Player;
*
* Bukkit: http://dev.bukkit.org/bukkit-plugins/xauth/
*/
public class xAuthHook implements BukkitAuthPlugin {
public class xAuthHook implements AuthPlugin<Player> {
protected final xAuth xAuthPlugin = xAuth.getPlugin();
private final xAuth xAuthPlugin = xAuth.getPlugin();
@Override
public boolean forceLogin(final Player player) {
public boolean forceLogin(Player player) {
//not thread-safe
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(xAuthPlugin, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(player);
if (xAuthPlayer != null) {
//we checked that the player is premium (paid account)
xAuthPlayer.setPremium(true);
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(xAuthPlugin, () -> {
xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(player);
if (xAuthPlayer != null) {
//we checked that the player is premium (paid account)
xAuthPlayer.setPremium(true);
//unprotect the inventory, op status...
return xAuthPlugin.getPlayerManager().doLogin(xAuthPlayer);
}
return false;
//unprotect the inventory, op status...
return xAuthPlugin.getPlayerManager().doLogin(xAuthPlayer);
}
return false;
});
try {
@@ -58,33 +56,25 @@ public class xAuthHook implements BukkitAuthPlugin {
}
@Override
public boolean forceRegister(final Player player, final String password) {
public boolean forceRegister(Player player, final String password) {
//not thread-safe
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(xAuthPlugin, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(player);
if (xAuthPlayer != null) {
//this should run async because the plugin executes a sql query, but the method
//accesses non thread-safe collections :(
boolean registerSuccess = xAuthPlugin.getAuthClass(xAuthPlayer)
.adminRegister(player.getName(), password, null);
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(xAuthPlugin, () -> {
xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(player);
if (xAuthPlayer != null) {
//this should run async because the plugin executes a sql query, but the method
//accesses non thread-safe collections :(
boolean registerSuccess = xAuthPlugin.getAuthClass(xAuthPlayer)
.adminRegister(player.getName(), password, null);
return registerSuccess;
}
return false;
}
});
try {
boolean success = future.get();
if (success) {
//login in the player after registration
return forceLogin(player);
return registerSuccess;
}
return false;
});
try {
//login in the player after registration
return future.get() && forceLogin(player);
} catch (InterruptedException | ExecutionException ex) {
xAuthPlugin.getLogger().log(Level.SEVERE, "Failed to forceLogin", ex);
return false;

View File

@@ -21,7 +21,7 @@ public class BukkitJoinListener implements Listener {
private static final long DELAY_LOGIN = 20L / 2;
protected final FastLoginBukkit plugin;
private final FastLoginBukkit plugin;
public BukkitJoinListener(FastLoginBukkit plugin) {
this.plugin = plugin;
@@ -48,6 +48,6 @@ public class BukkitJoinListener implements Listener {
public void onPlayerQuit(PlayerQuitEvent quitEvent) {
Player player = quitEvent.getPlayer();
player.removeMetadata(plugin.getName(), plugin);
plugin.getPendingConfirms().remove(player.getUniqueId());
plugin.getCore().getPendingConfirms().remove(player.getUniqueId());
}
}

View File

@@ -1,21 +1,21 @@
package com.github.games647.fastlogin.bukkit.listener;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.tasks.ForceLoginTask;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
import com.google.common.base.Charsets;
import com.google.common.collect.Sets;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@@ -32,7 +32,7 @@ public class BungeeCordListener implements PluginMessageListener {
private static final String FILE_NAME = "proxy-whitelist.txt";
protected final FastLoginBukkit plugin;
private final FastLoginBukkit plugin;
//null if whitelist is empty so bungeecord support is disabled
private final Set<UUID> proxyIds;
@@ -42,7 +42,7 @@ public class BungeeCordListener implements PluginMessageListener {
}
@Override
public void onPluginMessageReceived(String channel, final Player player, byte[] message) {
public void onPluginMessageReceived(String channel, Player player, byte[] message) {
if (!channel.equals(plugin.getName())) {
return;
}
@@ -52,10 +52,10 @@ public class BungeeCordListener implements PluginMessageListener {
plugin.getLogger().log(Level.FINEST, "Received plugin message for subchannel {0} from {1}"
, new Object[]{subchannel, player});
final String playerName = dataInput.readUTF();
String playerName = dataInput.readUTF();
//check if the player is still online or disconnected
final Player checkedPlayer = plugin.getServer().getPlayerExact(playerName);
Player checkedPlayer = plugin.getServer().getPlayerExact(playerName);
//fail if target player is blacklisted because already authed or wrong bungeecord id
if (checkedPlayer != null && !checkedPlayer.hasMetadata(plugin.getName())) {
//blacklist this target player for BungeeCord Id brute force attacks
@@ -69,58 +69,46 @@ public class BungeeCordListener implements PluginMessageListener {
//fail if BungeeCord support is disabled (id = null)
if (proxyIds.contains(sourceId)) {
final String id = '/' + checkedPlayer.getAddress().getAddress().getHostAddress() + ':'
+ checkedPlayer.getAddress().getPort();
if ("AUTO_LOGIN".equalsIgnoreCase(subchannel)) {
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, true);
playerSession.setVerified(true);
plugin.getSessions().put(id, playerSession);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new ForceLoginTask(plugin, player));
} else if ("AUTO_REGISTER".equalsIgnoreCase(subchannel)) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
try {
//we need to check if the player is registered on Bukkit too
if (authPlugin == null || !authPlugin.isRegistered(playerName)) {
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, false);
playerSession.setVerified(true);
plugin.getSessions().put(id, playerSession);
new ForceLoginTask(plugin, player).run();
}
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to query isRegistered", ex);
}
}
});
}
readMessage(checkedPlayer, subchannel, playerName, player);
}
}
}
private void readMessage(Player checkedPlayer, String subchannel, String playerName, Player player) {
InetSocketAddress address = checkedPlayer.getAddress();
String id = '/' + address.getAddress().getHostAddress() + ':' + address.getPort();
if ("AUTO_LOGIN".equalsIgnoreCase(subchannel)) {
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, true);
playerSession.setVerified(true);
plugin.getSessions().put(id, playerSession);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new ForceLoginTask(plugin, player));
} else if ("AUTO_REGISTER".equalsIgnoreCase(subchannel)) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook();
try {
//we need to check if the player is registered on Bukkit too
if (authPlugin == null || !authPlugin.isRegistered(playerName)) {
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, false);
playerSession.setVerified(true);
plugin.getSessions().put(id, playerSession);
new ForceLoginTask(plugin, player).run();
}
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to query isRegistered", ex);
}
});
}
}
public Set<UUID> loadBungeeCordIds() {
File whitelistFile = new File(plugin.getDataFolder(), FILE_NAME);
//create a new folder if it doesn't exist. Fail silently otherwise
whitelistFile.getParentFile().mkdir();
try {
if (!whitelistFile.exists()) {
whitelistFile.createNewFile();
}
Set<UUID> ids = Sets.newHashSet();
List<String> lines = Files.readLines(whitelistFile, Charsets.UTF_8);
for (String line : lines) {
if (line == null || line.trim().isEmpty()) {
continue;
}
UUID uuid = UUID.fromString(line.trim());
ids.add(uuid);
}
return ids;
List<String> lines = Files.readAllLines(whitelistFile.toPath());
return lines.stream().map(String::trim).map(UUID::fromString).collect(Collectors.toSet());
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to create file for Proxy whitelist", ex);
} catch (Exception ex) {

View File

@@ -1,5 +1,6 @@
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.reflect.MethodUtils;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
@@ -9,8 +10,6 @@ import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.logging.Level;
import org.bukkit.entity.Player;
@@ -18,13 +17,13 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
public class LoginSkinApplyListener implements Listener {
private static final Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
private static final MethodAccessor GET_PROPERTIES = Accessors.getMethodAcccessorOrNull(
GAME_PROFILE, "getProperties");
private static final MethodAccessor GET_PROPERTIES = Accessors.getMethodAccessor(GAME_PROFILE, "getProperties");
private final FastLoginBukkit plugin;
@@ -35,25 +34,29 @@ public class LoginSkinApplyListener implements Listener {
@EventHandler(priority = EventPriority.LOW)
//run this on the loginEvent to let skins plugins see the skin like in normal minecraft behaviour
public void onPlayerLogin(PlayerLoginEvent loginEvent) {
if (loginEvent.getResult() != Result.ALLOWED) {
return;
}
Player player = loginEvent.getPlayer();
if (plugin.getConfig().getBoolean("forwardSkin")) {
//go through every session, because player.getAddress is null
//loginEvent.getAddress is just a InetAddress not InetSocketAddres, so not unique enough
Collection<BukkitLoginSession> sessions = plugin.getSessions().values();
for (BukkitLoginSession session : sessions) {
for (BukkitLoginSession session : plugin.getSessions().values()) {
if (session.getUsername().equals(player.getName())) {
applySkin(player, session);
String signature = session.getSkinSignature();
String skinData = session.getEncodedSkinData();
applySkin(player, skinData, signature);
break;
}
}
}
}
private void applySkin(Player player, BukkitLoginSession session) {
private void applySkin(Player player, String skinData, String signature) {
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
String skinData = session.getEncodedSkinData();
String signature = session.getSkinSignature();
if (skinData != null && signature != null) {
WrappedSignedProperty skin = WrappedSignedProperty.fromValues("textures", skinData, signature);
try {
@@ -61,8 +64,7 @@ public class LoginSkinApplyListener implements Listener {
} catch (ClassCastException castException) {
Object map = GET_PROPERTIES.invoke(gameProfile.getHandle());
try {
Method putMethod = map.getClass().getMethod("put", Object.class, Object.class);
putMethod.invoke(map, "textures", skin.getHandle());
MethodUtils.invokeMethod(map, "put", new Object[]{"textures", skin.getHandle()});
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
plugin.getLogger().log(Level.SEVERE, "Error setting premium skin", ex);
}

View File

@@ -1,25 +1,18 @@
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.core.shared.JoinManagement;
import java.lang.reflect.InvocationTargetException;
import java.security.PublicKey;
import java.util.Random;
import java.util.UUID;
import java.util.logging.Level;
import org.bukkit.entity.Player;
public class NameCheckTask implements Runnable {
private static final int VERIFY_TOKEN_LENGTH = 4;
public class NameCheckTask extends JoinManagement<Player, ProtocolLibLoginSource> implements Runnable {
private final FastLoginBukkit plugin;
private final PacketEvent packetEvent;
@@ -30,6 +23,8 @@ public class NameCheckTask implements Runnable {
private final String username;
public NameCheckTask(FastLoginBukkit plugin, PacketEvent packetEvent, Random random, Player player, String username) {
super(plugin.getCore(), plugin.getCore().getAuthPluginHook());
this.plugin = plugin;
this.packetEvent = packetEvent;
this.random = random;
@@ -40,102 +35,40 @@ public class NameCheckTask implements Runnable {
@Override
public void run() {
try {
nameCheck();
super.onLogin(username, new ProtocolLibLoginSource(plugin, packetEvent, player, random));
} finally {
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
}
}
private void nameCheck() {
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(username);
if (profile == null) {
//minecraft server implementation
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161
@Override
public void requestPremiumLogin(ProtocolLibLoginSource source, PlayerProfile profile, String username, boolean registered) {
try {
source.setOnlineMode();
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Cannot send encryption packet. Falling back to cracked login", ex);
return;
}
if (profile.getUserId() == -1) {
UUID premiumUUID = null;
//user not exists in the db
try {
boolean isRegistered = plugin.getAuthPlugin().isRegistered(username);
if (plugin.getConfig().getBoolean("nameChangeCheck")
|| (plugin.getConfig().getBoolean("autoRegister") && !isRegistered)) {
premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username);
}
String ip = player.getAddress().getAddress().getHostAddress();
core.getPendingLogins().put(ip + username, new Object());
if (premiumUUID != null && plugin.getConfig().getBoolean("nameChangeCheck")) {
PlayerProfile uuidProfile = plugin.getCore().getStorage().loadProfile(premiumUUID);
if (uuidProfile != null) {
plugin.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
enablePremiumLogin(uuidProfile, false);
return;
}
}
String serverId = source.getServerId();
byte[] verify = source.getVerifyToken();
if (premiumUUID != null && plugin.getConfig().getBoolean("autoRegister") && !isRegistered) {
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
enablePremiumLogin(profile, false);
return;
}
//no premium check passed so we save it as a cracked player
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.getSessions().put(player.getAddress().toString(), loginSession);
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to query isRegistered", ex);
}
} else if (profile.isPremium()) {
enablePremiumLogin(profile, true);
} else {
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.getSessions().put(player.getAddress().toString(), loginSession);
BukkitLoginSession playerSession = new BukkitLoginSession(username, serverId, verify, registered, profile);
plugin.getSessions().put(player.getAddress().toString(), playerSession);
//cancel only if the player has a paid account otherwise login as normal offline player
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
packetEvent.setCancelled(true);
}
}
//minecraft server implementation
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161
private void enablePremiumLogin(PlayerProfile profile, boolean registered) {
//randomized server id to make sure the request is for our server
//this could be relevant http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/
String serverId = Long.toString(random.nextLong(), 16);
//generate a random token which should be the same when we receive it from the client
byte[] verify = new byte[VERIFY_TOKEN_LENGTH];
random.nextBytes(verify);
boolean success = sentEncryptionRequest(player, serverId, verify);
if (success) {
BukkitLoginSession playerSession = new BukkitLoginSession(username, serverId, verify, registered, profile);
plugin.getSessions().put(player.getAddress().toString(), playerSession);
//cancel only if the player has a paid account otherwise login as normal offline player
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
packetEvent.setCancelled(true);
}
}
}
private boolean sentEncryptionRequest(Player player, String serverId, byte[] verifyToken) {
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
try {
/**
* Packet Information: http://wiki.vg/Protocol#Encryption_Request
*
* ServerID="" (String) key=public server key verifyToken=random 4 byte array
*/
PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN);
newPacket.getStrings().write(0, serverId);
newPacket.getSpecificModifier(PublicKey.class).write(0, plugin.getServerKey().getPublic());
newPacket.getByteArrays().write(0, verifyToken);
//serverId is a empty string
protocolManager.sendServerPacket(player, newPacket);
return true;
} catch (InvocationTargetException ex) {
plugin.getLogger().log(Level.SEVERE, "Cannot send encryption packet. Falling back to normal login", ex);
}
return false;
@Override
public void startCrackedSession(ProtocolLibLoginSource source, PlayerProfile profile, String username) {
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.getSessions().put(player.getAddress().toString(), loginSession);
}
}

View File

@@ -0,0 +1,99 @@
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.shared.LoginSource;
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.security.PublicKey;
import java.util.Random;
import org.bukkit.entity.Player;
public class ProtocolLibLoginSource implements LoginSource {
private static final int VERIFY_TOKEN_LENGTH = 4;
private final FastLoginBukkit plugin;
private final PacketEvent packetEvent;
private final Player player;
private final Random random;
private String serverId;
private final byte[] verifyToken = new byte[VERIFY_TOKEN_LENGTH];
public ProtocolLibLoginSource(FastLoginBukkit plugin, PacketEvent packetEvent, Player player, Random random) {
this.plugin = plugin;
this.packetEvent = packetEvent;
this.player = player;
this.random = random;
}
@Override
public void setOnlineMode() throws Exception {
//randomized server id to make sure the request is for our server
//this could be relevant http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/
serverId = Long.toString(random.nextLong(), 16);
//generate a random token which should be the same when we receive it from the client
random.nextBytes(verifyToken);
sentEncryptionRequest();
}
@Override
public void kick(String message) throws Exception {
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
PacketContainer kickPacket = protocolManager.createPacket(PacketType.Login.Server.DISCONNECT);
kickPacket.getChatComponents().write(0, WrappedChatComponent.fromText(message));
try {
//send kick packet at login state
//the normal event.getPlayer.kickPlayer(String) method does only work at play state
protocolManager.sendServerPacket(player, kickPacket);
} finally {
//tell the server that we want to close the connection
player.kickPlayer("Disconnect");
}
}
@Override
public InetSocketAddress getAddress() {
return packetEvent.getPlayer().getAddress();
}
private void sentEncryptionRequest() throws InvocationTargetException {
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
/**
* Packet Information: http://wiki.vg/Protocol#Encryption_Request
*
* ServerID="" (String) key=public server key verifyToken=random 4 byte array
*/
PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN);
newPacket.getStrings().write(0, serverId);
newPacket.getSpecificModifier(PublicKey.class).write(0, plugin.getServerKey().getPublic());
newPacket.getByteArrays().write(0, verifyToken);
//serverId is a empty string
protocolManager.sendServerPacket(player, newPacket);
}
public String getServerId() {
return serverId;
}
public byte[] getVerifyToken() {
return verifyToken;
}
}

View File

@@ -5,12 +5,11 @@ import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
import java.util.Random;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
@@ -52,12 +51,11 @@ public class StartPacketListener extends PacketAdapter {
*/
@Override
public void onPacketReceiving(PacketEvent packetEvent) {
if (packetEvent.isCancelled()) {
if (packetEvent.isCancelled()
|| plugin.getCore().getAuthPluginHook()== null || !plugin.isServerFullyStarted()) {
return;
}
plugin.setServerStarted();
Player player = packetEvent.getPlayer();
//this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
@@ -72,11 +70,6 @@ public class StartPacketListener extends PacketAdapter {
String username = packet.getGameProfiles().read(0).getName();
plugin.getLogger().log(Level.FINER, "Player {0} with {1} connecting", new Object[]{sessionKey, username});
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
if (authPlugin == null) {
return;
}
packetEvent.getAsyncMarker().incrementProcessingDelay();
NameCheckTask nameCheckTask = new NameCheckTask(plugin, packetEvent, random, player, username);
Bukkit.getScheduler().runTaskAsynchronously(plugin, nameCheckTask);

View File

@@ -5,7 +5,9 @@ import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.netty.Injector;
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
@@ -13,7 +15,6 @@ import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.EncryptionUtil;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
@@ -50,9 +51,17 @@ public class VerifyResponseTask implements Runnable {
disconnect(plugin.getCore().getMessage("invalid-requst"), true
, "Player {0} tried to send encryption response at invalid state", fromPlayer.getAddress());
} else {
String ip = fromPlayer.getAddress().getAddress().getHostAddress();
plugin.getCore().getPendingLogins().remove(ip + session.getUsername());
verifyResponse(session);
}
} finally {
//this is a fake packet; it shouldn't be send to the server
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
packetEvent.setCancelled(true);
}
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
}
}
@@ -75,7 +84,7 @@ public class VerifyResponseTask implements Runnable {
String serverId = (new BigInteger(serverIdHash)).toString(16);
String username = session.getUsername();
if (plugin.getCore().getMojangApiConnector().hasJoinedServer(session, serverId)) {
if (plugin.getCore().getApiConnector().hasJoinedServer(session, serverId)) {
plugin.getLogger().log(Level.FINE, "Player {0} has a verified premium account", username);
session.setVerified(true);
@@ -87,11 +96,6 @@ public class VerifyResponseTask implements Runnable {
, "Player {0} ({1}) tried to log in with an invalid session ServerId: {2}"
, session.getUsername(), fromPlayer.getAddress(), serverId);
}
//this is a fake packet; it shouldn't be send to the server
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
packetEvent.setCancelled(true);
}
}
private void setPremiumUUID(UUID premiumUUID) {
@@ -99,10 +103,9 @@ public class VerifyResponseTask implements Runnable {
try {
Object networkManager = getNetworkManager();
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/NetworkManager.java#L69
Field spoofField = FuzzyReflection.fromObject(networkManager).getFieldByType("spoofedUUID", UUID.class);
spoofField.set(networkManager, premiumUUID);
} catch (ReflectiveOperationException reflectiveOperationException) {
plugin.getLogger().log(Level.SEVERE, "Error setting premium uuid", reflectiveOperationException);
FieldUtils.writeField(networkManager, "spoofedUUID", premiumUUID, true);
} catch (Exception exc) {
plugin.getLogger().log(Level.SEVERE, "Error setting premium uuid", exc);
}
}
}
@@ -126,15 +129,11 @@ public class VerifyResponseTask implements Runnable {
//try to get the networkManager from ProtocolLib
private Object getNetworkManager() throws IllegalAccessException, NoSuchFieldException {
Object socketInjector = TemporaryPlayerFactory.getInjectorFromPlayer(fromPlayer);
Field injectorField = socketInjector.getClass().getDeclaredField("injector");
injectorField.setAccessible(true);
Object injectorContainer = TemporaryPlayerFactory.getInjectorFromPlayer(fromPlayer);
Object rawInjector = injectorField.get(socketInjector);
injectorField = rawInjector.getClass().getDeclaredField("networkManager");
injectorField.setAccessible(true);
return injectorField.get(rawInjector);
//ChannelInjector
Injector rawInjector = FuzzyReflection.getFieldValue(injectorContainer, Injector.class, true);
return FieldUtils.readField(rawInjector, "networkManager", true);
}
private boolean encryptConnection(SecretKey loginKey) throws IllegalArgumentException {
@@ -143,14 +142,15 @@ public class VerifyResponseTask implements Runnable {
Object networkManager = getNetworkManager();
//try to detect the method by parameters
Method encryptConnectionMethod = FuzzyReflection
Method encryptMethod = FuzzyReflection
.fromObject(networkManager).getMethodByParameters("a", SecretKey.class);
//encrypt/decrypt following packets
//the client expects this behaviour
encryptConnectionMethod.invoke(networkManager, loginKey);
} catch (ReflectiveOperationException ex) {
disconnect(plugin.getCore().getMessage("error-kick"), false, "Couldn't enable encryption", ex);
encryptMethod.invoke(networkManager, loginKey);
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Couldn't enable encryption", ex);
disconnect(plugin.getCore().getMessage("error-kick"), false, "Couldn't enable encryption");
return false;
}
@@ -165,10 +165,6 @@ public class VerifyResponseTask implements Runnable {
}
kickPlayer(packetEvent.getPlayer(), kickReason);
//cancel the event in order to prevent the server receiving an invalid packet
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
packetEvent.setCancelled(true);
}
}
private void kickPlayer(Player player, String reason) {

View File

@@ -0,0 +1,35 @@
package com.github.games647.fastlogin.bukkit.listener.protocolsupport;
import com.github.games647.fastlogin.core.shared.LoginSource;
import java.net.InetSocketAddress;
import protocolsupport.api.events.PlayerLoginStartEvent;
public class ProtocolLoginSource implements LoginSource {
private final PlayerLoginStartEvent loginStartEvent;
public ProtocolLoginSource(PlayerLoginStartEvent loginStartEvent) {
this.loginStartEvent = loginStartEvent;
}
@Override
public void setOnlineMode() {
loginStartEvent.setOnlineMode(true);
}
@Override
public void kick(String message) {
loginStartEvent.denyLogin(message);
}
@Override
public InetSocketAddress getAddress() {
return loginStartEvent.getAddress();
}
public PlayerLoginStartEvent getLoginStartEvent() {
return loginStartEvent;
}
}

View File

@@ -2,108 +2,75 @@ package com.github.games647.fastlogin.bukkit.listener.protocolsupport;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
import com.github.games647.fastlogin.core.shared.JoinManagement;
import com.github.games647.fastlogin.core.PlayerProfile;
import java.net.InetSocketAddress;
import java.util.UUID;
import java.util.logging.Level;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import protocolsupport.api.events.PlayerLoginStartEvent;
import protocolsupport.api.events.PlayerPropertiesResolveEvent;
public class ProtocolSupportListener implements Listener {
public class ProtocolSupportListener extends JoinManagement<Player, ProtocolLoginSource> implements Listener {
protected final FastLoginBukkit plugin;
private final FastLoginBukkit plugin;
public ProtocolSupportListener(FastLoginBukkit plugin) {
super(plugin.getCore(), plugin.getCore().getAuthPluginHook());
this.plugin = plugin;
}
@EventHandler(ignoreCancelled = true)
@EventHandler
public void onLoginStart(PlayerLoginStartEvent loginStartEvent) {
plugin.setServerStarted();
if (loginStartEvent.isLoginDenied()) {
if (loginStartEvent.isLoginDenied() || plugin.getCore().getAuthPluginHook() == null) {
return;
}
String username = loginStartEvent.getName();
//remove old data every time on a new login in order to keep the session only for one person
plugin.getSessions().remove(loginStartEvent.getAddress().toString());
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
if (authPlugin == null) {
return;
}
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(username);
if (profile != null) {
if (profile.getUserId() == -1) {
UUID premiumUUID = null;
//user not exists in the db
try {
boolean isRegistered = plugin.getAuthPlugin().isRegistered(username);
if (plugin.getConfig().getBoolean("nameChangeCheck")
|| (plugin.getConfig().getBoolean("autoRegister") && !isRegistered)) {
premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username);
}
if (premiumUUID != null && plugin.getConfig().getBoolean("nameChangeCheck")) {
profile = plugin.getCore().getStorage().loadProfile(premiumUUID);
if (profile != null) {
plugin.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
startPremiumSession(username, loginStartEvent, false, profile);
return;
}
}
if (premiumUUID != null && plugin.getConfig().getBoolean("autoRegister") && !isRegistered) {
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
startPremiumSession(username, loginStartEvent, false, profile);
return;
}
//no premium check passed so we save it as a cracked player
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.getSessions().put(loginStartEvent.getAddress().toString(), loginSession);
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to query isRegistered", ex);
}
} else if (profile.isPremium()) {
startPremiumSession(username, loginStartEvent, true, profile);
} else {
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.getSessions().put(loginStartEvent.getAddress().toString(), loginSession);
}
}
}
@EventHandler(ignoreCancelled = true)
public void onPropertiesResolve(PlayerPropertiesResolveEvent propertiesResolveEvent) {
//skin was resolved -> premium player
if (propertiesResolveEvent.hasProperty("textures")) {
InetSocketAddress address = propertiesResolveEvent.getAddress();
BukkitLoginSession session = plugin.getSessions().get(address.toString());
if (session != null) {
session.setVerified(true);
}
}
}
private void startPremiumSession(String playerName, PlayerLoginStartEvent loginStartEvent, boolean registered
, PlayerProfile playerProfile) {
loginStartEvent.setOnlineMode(true);
InetSocketAddress address = loginStartEvent.getAddress();
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, null, null, registered, playerProfile);
plugin.getSessions().put(address.toString(), playerSession);
if (plugin.getConfig().getBoolean("premiumUuid")) {
loginStartEvent.setUseOnlineModeUUID(true);
//remove old data every time on a new login in order to keep the session only for one person
plugin.getSessions().remove(address.toString());
super.onLogin(username, new ProtocolLoginSource(loginStartEvent));
}
@EventHandler
public void onPropertiesResolve(PlayerPropertiesResolveEvent propertiesResolveEvent) {
InetSocketAddress address = propertiesResolveEvent.getAddress();
BukkitLoginSession session = plugin.getSessions().get(address.toString());
//skin was resolved -> premium player
if (propertiesResolveEvent.hasProperty("textures") && session != null) {
String ip = address.getAddress().getHostAddress();
plugin.getCore().getPendingLogins().remove(ip + session.getUsername());
session.setVerified(true);
}
}
@Override
public void requestPremiumLogin(ProtocolLoginSource source, PlayerProfile profile, String username, boolean registered) {
source.setOnlineMode();
String ip = source.getAddress().getAddress().getHostAddress();
plugin.getCore().getPendingLogins().put(ip + username, new Object());
BukkitLoginSession playerSession = new BukkitLoginSession(username, null, null, registered, profile);
plugin.getSessions().put(source.getAddress().toString(), playerSession);
if (plugin.getConfig().getBoolean("premiumUuid")) {
source.getLoginStartEvent().setUseOnlineModeUUID(true);
}
}
@Override
public void startCrackedSession(ProtocolLoginSource source, PlayerProfile profile, String username) {
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.getSessions().put(source.getAddress().toString(), loginSession);
}
}

View File

@@ -2,18 +2,19 @@ package com.github.games647.fastlogin.bukkit.tasks;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.hooks.AuthMeHook;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
import com.github.games647.fastlogin.bukkit.hooks.CrazyLoginHook;
import com.github.games647.fastlogin.bukkit.hooks.LogItHook;
import com.github.games647.fastlogin.bukkit.hooks.LoginSecurityHook;
import com.github.games647.fastlogin.bukkit.hooks.UltraAuthHook;
import com.github.games647.fastlogin.bukkit.hooks.xAuthHook;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class DelayedAuthHook implements Runnable {
@@ -37,12 +38,14 @@ public class DelayedAuthHook implements Runnable {
}
private boolean registerHooks() {
BukkitAuthPlugin authPluginHook = null;
AuthPlugin<Player> authPluginHook = null;
try {
List<Class<? extends BukkitAuthPlugin>> supportedHooks = Lists.newArrayList(AuthMeHook.class
@SuppressWarnings("unchecked")
List<Class<? extends AuthPlugin<Player>>> supportedHooks = Lists.newArrayList(AuthMeHook.class
, CrazyLoginHook.class, LogItHook.class, LoginSecurityHook.class, UltraAuthHook.class
, xAuthHook.class);
for (Class<? extends BukkitAuthPlugin> clazz : supportedHooks) {
for (Class<? extends AuthPlugin<Player>> clazz : supportedHooks) {
String pluginName = clazz.getSimpleName().replace("Hook", "");
//uses only member classes which uses AuthPlugin interface (skip interfaces)
if (Bukkit.getServer().getPluginManager().getPlugin(pluginName) != null) {
@@ -62,8 +65,9 @@ public class DelayedAuthHook implements Runnable {
return false;
}
if (plugin.getAuthPlugin() == null) {
plugin.setAuthPluginHook(authPluginHook);
if (plugin.getCore().getAuthPluginHook() == null) {
plugin.getCore().setAuthPluginHook(authPluginHook);
plugin.setServerStarted();
}
return true;

View File

@@ -2,13 +2,12 @@ package com.github.games647.fastlogin.bukkit.tasks;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
import com.github.games647.fastlogin.core.AuthStorage;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
@@ -19,7 +18,7 @@ import org.bukkit.entity.Player;
public class ForceLoginTask implements Runnable {
private final FastLoginBukkit plugin;
protected final Player player;
private final Player player;
public ForceLoginTask(FastLoginBukkit plugin, Player player) {
this.plugin = plugin;
@@ -45,17 +44,21 @@ public class ForceLoginTask implements Runnable {
//check if it's the same player as we checked before
if (session.isVerified() && player.getName().equals(session.getUsername())) {
//premium player
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook();
if (authPlugin == null) {
//maybe only bungeecord plugin
sendSuccessNotification();
} else {
boolean success = false;
if (isOnlineThreadSafe()) {
if (session.needsRegistration()) {
success = forceRegister(authPlugin, player);
if (plugin.getConfig().getBoolean("autoLogin")) {
if (session.needsRegistration()) {
success = forceRegister(authPlugin, player);
} else {
success = forceLogin(authPlugin, player);
}
} else {
success = forceLogin(authPlugin, player);
success = true;
}
}
@@ -81,10 +84,10 @@ public class ForceLoginTask implements Runnable {
}
}
private boolean forceRegister(BukkitAuthPlugin authPlugin, Player player) {
private boolean forceRegister(AuthPlugin<Player> authPlugin, Player player) {
plugin.getLogger().log(Level.FINE, "Register player {0}", player.getName());
String generatedPassword = plugin.generateStringPassword(player);
String generatedPassword = plugin.getCore().getPasswordGenerator().getRandomPassword(player);
boolean success = authPlugin.forceRegister(player, generatedPassword);
String message = plugin.getCore().getMessage("auto-register");
if (success && message != null) {
@@ -95,7 +98,7 @@ public class ForceLoginTask implements Runnable {
return success;
}
private boolean forceLogin(BukkitAuthPlugin authPlugin, Player player) {
private boolean forceLogin(AuthPlugin<Player> authPlugin, Player player) {
plugin.getLogger().log(Level.FINE, "Logging player {0} in", player.getName());
boolean success = authPlugin.forceLogin(player);
@@ -118,12 +121,7 @@ public class ForceLoginTask implements Runnable {
private boolean isOnlineThreadSafe() {
//the playerlist isn't thread-safe
Future<Boolean> onlineFuture = Bukkit.getScheduler().callSyncMethod(plugin, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return player.isOnline();
}
});
Future<Boolean> onlineFuture = Bukkit.getScheduler().callSyncMethod(plugin, player::isOnline);
try {
return onlineFuture.get();

View File

@@ -15,17 +15,9 @@ dev-url: ${project.url}
load: STARTUP
softdepend:
# We require either ProtocolLib or ProtocolSupport
# We depend either ProtocolLib or ProtocolSupport
- ProtocolSupport
- ProtocolLib
# Auth plugins - we delay the hook
# - xAuth
# - AuthMe
# - LogIt
# - CrazyLogin
# - LoginSecurity
# - RoyalAuth
# - UltraAuth
commands:
${project.parent.name}:
@@ -38,7 +30,12 @@ commands:
description: 'Label the invoker or the player specified as cracked if he was marked premium before'
aliases: [unpremium]
usage: /<command> [player]
permission: ${project.artifactId}.command.unpremium
permission: ${project.artifactId}.command.cracked
import-auth:
description: 'Imports the auth data from another auto login'
usage: /<command> [player]
permission: ${project.artifactId}.command.import
permissions:
${project.artifactId}.command.premium:
@@ -54,7 +51,7 @@ permissions:
description: 'Label themselves as cracked'
default: true
${project.artifactId}.command..cracked.other:
${project.artifactId}.command.cracked.other:
description: 'Label others as cracked'
children:
${project.artifactId}.command.cracked: true

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.7.1</version>
<version>1.9</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -23,10 +23,9 @@
<url>http://maven.myplayplanet.net/</url>
</repository>
<!--Github automatic maven builds-->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
<id>vik1395-repo</id>
<url>http://repo.vik1395.me/repositories</url>
</repository>
</repositories>
@@ -44,18 +43,11 @@
<version>1.8-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- <dependency>
<groupId>io.github.waterfallmc</groupId>
<artifactId>waterfall-api</artifactId>
<version>1.9-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
</dependency>-->
<dependency>
<groupId>com.github.MatteCarra</groupId>
<groupId>me.vik1395</groupId>
<artifactId>BungeeAuth</artifactId>
<version>-1.2.1-gc367d92-8</version>
<version>1.3.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,27 +1,38 @@
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.core.FastLoginCore;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.MojangApiConnector;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.md_5.bungee.api.ChatColor;
import java.util.stream.Collectors;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
public class BungeeCore extends FastLoginCore {
public class BungeeCore extends FastLoginCore<ProxiedPlayer> {
private static Map<String, Object> generateConfigMap(Configuration config) {
return config.getKeys().stream()
.filter(key -> config.get(key) != null)
.collect(Collectors.toMap(key -> key, config::get));
}
private final FastLoginBungee plugin;
public BungeeCore(FastLoginBungee plugin) {
public BungeeCore(FastLoginBungee plugin, Configuration config) {
super(generateConfigMap(config));
this.plugin = plugin;
}
@@ -36,54 +47,41 @@ public class BungeeCore extends FastLoginCore {
}
@Override
@SuppressWarnings("deprecation")
public ThreadFactory getThreadFactory() {
String pluginName = plugin.getDescription().getName();
return new ThreadFactoryBuilder()
.setNameFormat(pluginName + " Database Pool Thread #%1$d")
//Hikari create daemons by default
.setDaemon(true)
.setThreadFactory(new GroupedThreadFactory(plugin, pluginName)).build();
.setThreadFactory(new GroupedThreadFactory(plugin, pluginName))
.build();
}
@Override
public void loadMessages() {
try {
saveDefaultFile("messages.yml");
plugin.saveDefaultFile("messages.yml");
ConfigurationProvider configProvider = ConfigurationProvider.getProvider(YamlConfiguration.class);
Configuration defaults = ConfigurationProvider.getProvider(YamlConfiguration.class)
.load(getClass().getResourceAsStream("/messages.yml"));
Configuration defaults = configProvider.load(getClass().getResourceAsStream("/messages.yml"));
File messageFile = new File(getDataFolder(), "messages.yml");
Configuration messageConfig = ConfigurationProvider.getProvider(YamlConfiguration.class)
.load(messageFile, defaults);
for (String key : messageConfig.getKeys()) {
Configuration messageConfig = configProvider.load(messageFile, defaults);
messageConfig.getKeys().forEach(key -> {
String message = ChatColor.translateAlternateColorCodes('&', messageConfig.getString(key));
if (!message.isEmpty()) {
localeMessages.put(key, message);
}
}
});
} catch (IOException ex) {
getLogger().log(Level.SEVERE, "Failed to load messages", ex);
}
}
@Override
public void loadConfig() {
if (!getDataFolder().exists()) {
getDataFolder().mkdir();
}
saveDefaultFile("config.yml");
}
private void saveDefaultFile(String fileName) {
File configFile = new File(getDataFolder(), fileName);
if (!configFile.exists()) {
try (InputStream in = plugin.getResourceAsStream(fileName)) {
Files.copy(in, configFile.toPath());
} catch (IOException ioExc) {
getLogger().log(Level.SEVERE, "Error saving default " + fileName, ioExc);
}
}
public MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests) {
return new MojangApiBungee(logger, addresses, requests);
}
}

View File

@@ -1,11 +1,12 @@
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.core.LoginSession;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.core.shared.LoginSession;
public class BungeeLoginSession extends LoginSession {
private boolean alreadySaved;
private boolean alreadyLogged;
public BungeeLoginSession(String username, boolean registered, PlayerProfile profile) {
super(username, registered, profile);
@@ -22,4 +23,12 @@ public class BungeeLoginSession extends LoginSession {
public void setAlreadySaved(boolean alreadySaved) {
this.alreadySaved = alreadySaved;
}
public boolean isAlreadyLogged() {
return alreadyLogged;
}
public void setAlreadyLogged(boolean alreadyLogged) {
this.alreadyLogged = alreadyLogged;
}
}

View File

@@ -0,0 +1,36 @@
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.core.shared.LoginSource;
import java.net.InetSocketAddress;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
public class BungeeLoginSource implements LoginSource {
private final PendingConnection connection;
public BungeeLoginSource(PendingConnection connection) {
this.connection = connection;
}
@Override
public void setOnlineMode() {
connection.setOnlineMode(true);
}
@Override
public void kick(String message) {
connection.disconnect(TextComponent.fromLegacyText(message));
}
@Override
public InetSocketAddress getAddress() {
return connection.getAddress();
}
public PendingConnection getConnection() {
return connection;
}
}

View File

@@ -4,19 +4,13 @@ import com.github.games647.fastlogin.bungee.hooks.BungeeAuthHook;
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
import com.github.games647.fastlogin.bungee.listener.PlayerConnectionListener;
import com.github.games647.fastlogin.bungee.listener.PluginMessageListener;
import com.github.games647.fastlogin.core.FastLoginCore;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import net.md_5.bungee.api.connection.PendingConnection;
@@ -30,42 +24,23 @@ import net.md_5.bungee.config.YamlConfiguration;
*/
public class FastLoginBungee extends Plugin {
private static final char[] PASSWORD_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
.toCharArray();
private final FastLoginCore loginCore = new BungeeCore(this);
private BungeeAuthPlugin bungeeAuthPlugin;
private Configuration config;
private final Random random = new Random();
private final Set<UUID> pendingConfirms = Sets.newHashSet();
private final ConcurrentMap<PendingConnection, BungeeLoginSession> session = Maps.newConcurrentMap();
private BungeeCore core;
private Configuration config;
@Override
public void onEnable() {
loginCore.loadConfig();
loginCore.loadMessages();
saveDefaultFile("config.yml");
try {
File configFile = new File(getDataFolder(), "config.yml");
config = ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile);
ConfigurationProvider provider = ConfigurationProvider.getProvider(YamlConfiguration.class);
Configuration defaults = provider.load(getResourceAsStream("config.yml"));
config = provider.load(configFile, defaults);
List<String> ipAddresses = getConfig().getStringList("ip-addresses");
int requestLimit = getConfig().getInt("mojang-request-limit");
ConcurrentMap<Object, Object> requestCache = CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES).build().asMap();
MojangApiBungee mojangApi = new MojangApiBungee(requestCache, getLogger(), ipAddresses, requestLimit);
loginCore.setMojangApiConnector(mojangApi);
String driver = config.getString("driver");
String host = config.getString("host", "");
int port = config.getInt("port", 3306);
String database = config.getString("database");
String username = config.getString("username", "");
String password = config.getString("password", "");
if (!loginCore.setupDatabase(driver, host, port, database, username, password)) {
core = new BungeeCore(this, config);
if (!core.setupDatabase()) {
return;
}
} catch (IOException ioExc) {
@@ -73,36 +48,58 @@ public class FastLoginBungee extends Plugin {
return;
}
core.setApiConnector();
core.loadMessages();
//events
getProxy().getPluginManager().registerListener(this, new PlayerConnectionListener(this));
getProxy().getPluginManager().registerListener(this, new PluginMessageListener(this));
//bungee only commands
getProxy().getPluginManager().registerCommand(this, new ImportCommand(this));
//this is required to listen to messages from the server
getProxy().registerChannel(getDescription().getName());
registerHook();
}
public String generateStringPassword() {
StringBuilder generatedPassword = new StringBuilder(8);
for (int i = 1; i <= 8; i++) {
generatedPassword.append(PASSWORD_CHARACTERS[random.nextInt(PASSWORD_CHARACTERS.length - 1)]);
}
return generatedPassword.toString();
}
@Override
public void onDisable() {
loginCore.close();
if (core != null) {
core.close();
}
}
public FastLoginCore getCore() {
return loginCore;
public void saveDefaultFile(String fileName) {
if (!getDataFolder().exists()) {
getDataFolder().mkdir();
}
File configFile = new File(getDataFolder(), fileName);
if (!configFile.exists()) {
InputStream in = getResourceAsStream(fileName);
try {
Files.copy(in, configFile.toPath());
} catch (IOException ioExc) {
getLogger().log(Level.SEVERE, "Error saving default " + fileName, ioExc);
} finally {
try {
in.close();
} catch (IOException ex) {
getLogger().log(Level.SEVERE, null, ex);
}
}
}
}
public BungeeCore getCore() {
return core;
}
@Deprecated
public void setAuthPluginHook(BungeeAuthPlugin authPlugin) {
this.bungeeAuthPlugin = authPlugin;
core.setAuthPluginHook(authPlugin);
}
public Configuration getConfig() {
@@ -113,23 +110,20 @@ public class FastLoginBungee extends Plugin {
return session;
}
public Set<UUID> getPendingConfirms() {
return pendingConfirms;
}
/**
* Get the auth plugin hook for BungeeCord
*
* @return the auth hook for BungeeCord. null if none found
*/
@Deprecated
public BungeeAuthPlugin getBungeeAuthPlugin() {
return bungeeAuthPlugin;
return (BungeeAuthPlugin) core.getAuthPluginHook();
}
private void registerHook() {
Plugin plugin = getProxy().getPluginManager().getPlugin("BungeeAuth");
if (plugin != null) {
bungeeAuthPlugin = new BungeeAuthHook();
core.setAuthPluginHook(new BungeeAuthHook());
getLogger().info("Hooked into BungeeAuth");
}
}

View File

@@ -0,0 +1,91 @@
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.core.AuthStorage;
import com.github.games647.fastlogin.core.importer.ImportPlugin;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Command;
public class ImportCommand extends Command {
private final BungeeCore core;
public ImportCommand(FastLoginBungee plugin) {
super("import-db", plugin.getDescription().getName().toLowerCase() + ".import");
this.core = plugin.getCore();
}
@Override
public void execute(CommandSender sender, String[] args) {
if (args.length < 2) {
String message = ChatColor.DARK_RED + "You need to specify the import plugin and database type";
sender.sendMessage(convertFromLegacy(message));
return;
}
ImportPlugin importPlugin;
switch (args[0].toLowerCase()) {
case "autoin":
importPlugin = ImportPlugin.AUTO_IN;
break;
case "bpa":
importPlugin = ImportPlugin.BPA;
break;
case "eldzi":
importPlugin = ImportPlugin.ELDZI;
break;
default:
String message = ChatColor.DARK_RED + "Unknown auto login plugin";
sender.sendMessage(convertFromLegacy(message));
return;
}
boolean sqlite;
switch (args[1].toLowerCase()) {
case "sqlite":
sqlite = true;
break;
case "mysql":
sqlite = false;
break;
default:
String message = ChatColor.DARK_RED + "Unknown storage type to import from. Either SQLite or MySQL";
sender.sendMessage(convertFromLegacy(message));
return;
}
String host = "";
String database = "";
String username = "";
String password = "";
if (!sqlite) {
if (args.length <= 5) {
String message = ChatColor.DARK_RED + "If importing from MySQL, you need to specify host database "
+ "and username passowrd too";
sender.sendMessage(convertFromLegacy(message));
return;
}
host = args[2];
database = args[3];
username = args[4];
password = args[5];
}
AuthStorage storage = core.getStorage();
boolean success = core.importDatabase(importPlugin, true, storage, host, database, username, password);
if (success) {
sender.sendMessage(convertFromLegacy(ChatColor.DARK_GREEN + "Successful imported the data"));
} else {
sender.sendMessage(convertFromLegacy(ChatColor.DARK_RED + "Failed to import the data. Check out the logs"));
}
}
private BaseComponent[] convertFromLegacy(String message) {
return TextComponent.fromLegacyText(message);
}
}

View File

@@ -1,24 +1,21 @@
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.core.FastLoginCore;
import com.github.games647.fastlogin.core.MojangApiConnector;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.shared.MojangApiConnector;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Logger;
import net.md_5.bungee.BungeeCord;
public class MojangApiBungee extends MojangApiConnector {
public MojangApiBungee(ConcurrentMap<Object, Object> requests, Logger logger, List<String> localAddresses
, int rateLimit) {
super(requests, logger, localAddresses, rateLimit);
public MojangApiBungee(Logger logger, List<String> localAddresses, int rateLimit) {
super(logger, localAddresses, rateLimit);
}
@Override
protected UUID getUUIDFromJson(String json) {
protected String getUUIDFromJson(String json) {
boolean isArray = json.startsWith("[");
MojangPlayer mojangPlayer;
@@ -28,15 +25,16 @@ public class MojangApiBungee extends MojangApiConnector {
mojangPlayer = BungeeCord.getInstance().gson.fromJson(json, MojangPlayer.class);
}
if (mojangPlayer.getId() == null || mojangPlayer.getId().equals("null")) {
String id = mojangPlayer.getId();
if ("null".equals(id)) {
return null;
}
return FastLoginCore.parseId(mojangPlayer.getId());
return id;
}
@Override
public boolean hasJoinedServer(Object session, String serverId) {
public boolean hasJoinedServer(LoginSession session, String serverId) {
//this is not needed in Bungee
throw new UnsupportedOperationException("Not supported");
}

View File

@@ -1,5 +1,7 @@
package com.github.games647.fastlogin.bungee.hooks;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -20,27 +22,32 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
*
* Spigot: https://www.spigotmc.org/resources/bungeeauth.493/
*/
public class BungeeAuthHook implements BungeeAuthPlugin {
public class BungeeAuthHook implements AuthPlugin<ProxiedPlayer> {
//https://github.com/MatteCarra/BungeeAuth/blob/master/src/me/vik1395/BungeeAuth/Login.java#L32
private final Tables databaseConnection = new Tables();
@Override
public boolean forceLogin(ProxiedPlayer player) {
String playerName = player.getName();
//https://github.com/MatteCarra/BungeeAuth/blob/master/src/me/vik1395/BungeeAuth/Login.java#L92-95
Main.plonline.add(player.getName());
if (Main.plonline.contains(playerName)) {
return true;
}
Main.plonline.add(playerName);
//renamed from ct to databaseConnection
// databaseConnection.setStatus(player.getName(), "online");
Class<?>[] parameterTypes = new Class<?>[]{String.class, String.class};
Object[] arguments = new Object[]{player.getName(), "online"};
Object[] arguments = new Object[]{playerName, "online"};
try {
callProtected("setStatus", parameterTypes, arguments);
ListenerClass.movePlayer(player, false);
//proparly not thread-safe
ListenerClass.prelogin.get(player.getName()).cancel();
ListenerClass.prelogin.get(playerName).cancel();
} catch (Exception ex) {
Main.plugin.getLogger().log(Level.SEVERE, "Error force loging in player", ex);
return false;
@@ -74,15 +81,12 @@ public class BungeeAuthHook implements BungeeAuthPlugin {
String hash = ph.newHash(Pw, pType);
//creates a new SQL entry with the player's details.
//renamed t to databaseConnection
// databaseConnection.newPlayerEntry(player.getName(), hash, pType, "", lastip, regdate, lastip, lastseen);
Class<?>[] parameterTypes = new Class<?>[] {String.class, String.class, String.class, String.class
, String.class, String.class, String.class, String.class};
Object[] arguments = new Object[] {player.getName(), hash, pType, "", lastip, regdate, lastip, lastseen};
try {
callProtected("newPlayerEntry", parameterTypes, arguments);
//proparly not thread-safe
forceLogin(player);
@@ -100,6 +104,8 @@ public class BungeeAuthHook implements BungeeAuthPlugin {
Method method = tableClass.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
//renamed t to databaseConnection
//databaseConnection.newPlayerEntry(player.getName(), hash, pType, "", lastip, regdate, lastip, lastseen);
method.invoke(databaseConnection, arguments);
}
}

View File

@@ -1,55 +1,20 @@
package com.github.games647.fastlogin.bungee.hooks;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import net.md_5.bungee.api.connection.ProxiedPlayer;
/**
* Represents a supporting authentication plugin in BungeeCord/Waterfall/... servers
* @deprecated please use com.github.games647.fastlogin.core.hooks.AuthPlugin<net.md_5.bungee.api.connection.ProxiedPlayer>
*/
public interface BungeeAuthPlugin {
@Deprecated
public interface BungeeAuthPlugin extends AuthPlugin<ProxiedPlayer> {
/**
* Login the premium (paid account) player after
* the player joined successfully a server.
*
* @param player the player that needs to be logged in
* @return if the operation was successful
*/
@Override
boolean forceLogin(ProxiedPlayer player);
/**
* 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
*/
@Override
boolean isRegistered(String playerName) throws Exception;
/**
* Forces a register in order to protect the paid account.
* The method will be invoked after the player joined a server.
*
* 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.
*
* 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
*/
@Override
boolean forceRegister(ProxiedPlayer player, String password);
}

View File

@@ -3,8 +3,8 @@ package com.github.games647.fastlogin.bungee.listener;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.tasks.AsyncPremiumCheck;
import com.github.games647.fastlogin.bungee.tasks.ForceLoginTask;
import com.github.games647.fastlogin.core.LoginSession;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.google.common.base.Charsets;
import java.lang.reflect.Field;
@@ -32,7 +32,7 @@ import net.md_5.bungee.event.EventPriority;
*/
public class PlayerConnectionListener implements Listener {
protected final FastLoginBungee plugin;
private final FastLoginBungee plugin;
public PlayerConnectionListener(FastLoginBungee plugin) {
this.plugin = plugin;
@@ -45,7 +45,10 @@ public class PlayerConnectionListener implements Listener {
}
preLoginEvent.registerIntent(plugin);
ProxyServer.getInstance().getScheduler().runAsync(plugin, new AsyncPremiumCheck(plugin, preLoginEvent));
PendingConnection connection = preLoginEvent.getConnection();
AsyncPremiumCheck asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection);
ProxyServer.getInstance().getScheduler().runAsync(plugin, asyncPremiumCheck);
}
@EventHandler(priority = EventPriority.LOW)
@@ -59,7 +62,12 @@ public class PlayerConnectionListener implements Listener {
PendingConnection connection = loginEvent.getConnection();
String username = connection.getName();
if (connection.isOnlineMode()) {
String ip = connection.getAddress().getAddress().getHostAddress();
plugin.getCore().getPendingLogins().remove(ip + username);
LoginSession session = plugin.getSession().get(connection);
session.setUuid(connection.getUniqueId());
PlayerProfile playerProfile = session.getProfile();
playerProfile.setUuid(connection.getUniqueId());
@@ -100,6 +108,6 @@ public class PlayerConnectionListener implements Listener {
public void onDisconnect(PlayerDisconnectEvent disconnectEvent) {
ProxiedPlayer player = disconnectEvent.getPlayer();
plugin.getSession().remove(player.getPendingConnection());
plugin.getPendingConfirms().remove(player.getUniqueId());
plugin.getCore().getPendingConfirms().remove(player.getUniqueId());
}
}

View File

@@ -6,6 +6,7 @@ import com.github.games647.fastlogin.bungee.tasks.AsyncToggleMessage;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import java.util.Arrays;
import net.md_5.bungee.api.ProxyServer;
@@ -18,7 +19,7 @@ import net.md_5.bungee.event.EventHandler;
public class PluginMessageListener implements Listener {
protected final FastLoginBungee plugin;
private final FastLoginBungee plugin;
public PluginMessageListener(FastLoginBungee plugin) {
this.plugin = plugin;
@@ -37,55 +38,54 @@ public class PluginMessageListener implements Listener {
//check if the message is sent from the server
if (Server.class.isAssignableFrom(pluginMessageEvent.getSender().getClass())) {
readMessage(pluginMessageEvent);
//so that we can safely process this in the background
byte[] data = Arrays.copyOf(pluginMessageEvent.getData(), pluginMessageEvent.getData().length);
ProxiedPlayer forPlayer = (ProxiedPlayer) pluginMessageEvent.getReceiver();
ProxyServer.getInstance().getScheduler().runAsync(plugin, () -> readMessage(forPlayer, data));
}
}
private void readMessage(PluginMessageEvent pluginMessageEvent) {
//so that we can safely process this in the background
final byte[] data = Arrays.copyOf(pluginMessageEvent.getData(), pluginMessageEvent.getData().length);
final ProxiedPlayer forPlayer = (ProxiedPlayer) pluginMessageEvent.getReceiver();
private void readMessage(ProxiedPlayer forPlayer, byte[] data) {
ByteArrayDataInput dataInput = ByteStreams.newDataInput(data);
String subchannel = dataInput.readUTF();
if ("ON".equals(subchannel)) {
String playerName = dataInput.readUTF();
ProxyServer.getInstance().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
ByteArrayDataInput dataInput = ByteStreams.newDataInput(data);
String subchannel = dataInput.readUTF();
if ("ON".equals(subchannel)) {
String playerName = dataInput.readUTF();
if (playerName.equals(forPlayer.getName()) && plugin.getConfig().getBoolean("premium-warning")
&& !plugin.getPendingConfirms().contains(forPlayer.getUniqueId())) {
String message = plugin.getCore().getMessage("premium-warning");
forPlayer.sendMessage(TextComponent.fromLegacyText(message));
plugin.getPendingConfirms().add(forPlayer.getUniqueId());
return;
}
plugin.getPendingConfirms().remove(forPlayer.getUniqueId());
AsyncToggleMessage task = new AsyncToggleMessage(plugin, forPlayer, playerName, true);
ProxyServer.getInstance().getScheduler().runAsync(plugin, task);
} else if ("OFF".equals(subchannel)) {
String playerName = dataInput.readUTF();
AsyncToggleMessage task = new AsyncToggleMessage(plugin, forPlayer, playerName, false);
ProxyServer.getInstance().getScheduler().runAsync(plugin, task);
} else if ("SUCCESS".equals(subchannel)) {
if (forPlayer.getPendingConnection().isOnlineMode()) {
//bukkit module successfully received and force logged in the user
//update only on success to prevent corrupt data
BungeeLoginSession loginSession = plugin.getSession().get(forPlayer.getPendingConnection());
PlayerProfile playerProfile = loginSession.getProfile();
loginSession.setRegistered(true);
if (!loginSession.isAlreadySaved()) {
playerProfile.setPremium(true);
plugin.getCore().getStorage().save(playerProfile);
loginSession.setAlreadySaved(true);
}
}
}
if (playerName.equals(forPlayer.getName()) && plugin.getConfig().getBoolean("premium-warning")
&& !plugin.getCore().getPendingConfirms().contains(forPlayer.getUniqueId())) {
String message = plugin.getCore().getMessage("premium-warning");
forPlayer.sendMessage(TextComponent.fromLegacyText(message));
plugin.getCore().getPendingConfirms().add(forPlayer.getUniqueId());
return;
}
});
plugin.getCore().getPendingConfirms().remove(forPlayer.getUniqueId());
AsyncToggleMessage task = new AsyncToggleMessage(plugin.getCore(), forPlayer, playerName, true);
ProxyServer.getInstance().getScheduler().runAsync(plugin, task);
} else if ("OFF".equals(subchannel)) {
String playerName = dataInput.readUTF();
AsyncToggleMessage task = new AsyncToggleMessage(plugin.getCore(), forPlayer, playerName, false);
ProxyServer.getInstance().getScheduler().runAsync(plugin, task);
} else if ("SUCCESS".equals(subchannel)) {
onSuccessMessage(forPlayer);
}
}
private void onSuccessMessage(ProxiedPlayer forPlayer) {
if (forPlayer.getPendingConnection().isOnlineMode()) {
//bukkit module successfully received and force logged in the user
//update only on success to prevent corrupt data
BungeeLoginSession loginSession = plugin.getSession().get(forPlayer.getPendingConnection());
PlayerProfile playerProfile = loginSession.getProfile();
loginSession.setRegistered(true);
if (!loginSession.isAlreadySaved()) {
playerProfile.setPremium(true);
plugin.getCore().getStorage().save(playerProfile);
loginSession.setAlreadySaved(true);
}
}
}
}

View File

@@ -1,93 +1,53 @@
package com.github.games647.fastlogin.bungee.tasks;
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
import com.github.games647.fastlogin.bungee.BungeeLoginSource;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
import com.github.games647.fastlogin.core.PlayerProfile;
import java.util.UUID;
import java.util.logging.Level;
import com.github.games647.fastlogin.core.shared.JoinManagement;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.event.PreLoginEvent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.AsyncEvent;
public class AsyncPremiumCheck implements Runnable {
public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, BungeeLoginSource> implements Runnable {
private final FastLoginBungee plugin;
private final PreLoginEvent preLoginEvent;
private final AsyncEvent<?> preLoginEvent;
private final PendingConnection connection;
public AsyncPremiumCheck(FastLoginBungee plugin, AsyncEvent<?> preLoginEvent, PendingConnection connection) {
super(plugin.getCore(), plugin.getCore().getAuthPluginHook());
public AsyncPremiumCheck(FastLoginBungee plugin, PreLoginEvent preLoginEvent) {
this.plugin = plugin;
this.preLoginEvent = preLoginEvent;
this.connection = connection;
}
@Override
public void run() {
PendingConnection connection = preLoginEvent.getConnection();
plugin.getSession().remove(connection);
String username = connection.getName();
try {
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(username);
if (profile == null) {
return;
}
if (profile.getUserId() == -1) {
UUID premiumUUID = null;
if (plugin.getConfig().getBoolean("nameChangeCheck") || plugin.getConfig().getBoolean("autoRegister")) {
premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username);
}
if (premiumUUID == null
|| (!checkNameChange(premiumUUID, connection, username)
&& !checkPremiumName(username, connection, profile))) {
//nothing detected the player as premium -> start a cracked session
plugin.getSession().put(connection, new BungeeLoginSession(username, false, profile));
}
} else if (profile.isPremium()) {
requestPremiumLogin(connection, profile, username, true);
} else {
//Cracked session
plugin.getSession().put(connection, new BungeeLoginSession(username, false, profile));
}
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to check premium state", ex);
super.onLogin(username, new BungeeLoginSource(connection));
} finally {
preLoginEvent.completeIntent(plugin);
}
}
private boolean checkPremiumName(String username, PendingConnection connection, PlayerProfile profile)
throws Exception {
BungeeAuthPlugin authPlugin = plugin.getBungeeAuthPlugin();
if (plugin.getConfig().getBoolean("autoRegister")
&& (authPlugin == null || !authPlugin.isRegistered(username))) {
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
requestPremiumLogin(connection, profile, username, false);
return true;
}
@Override
public void requestPremiumLogin(BungeeLoginSource source, PlayerProfile profile, String username, boolean registered) {
source.setOnlineMode();
plugin.getSession().put(source.getConnection(), new BungeeLoginSession(username, registered, profile));
return false;
String ip = source.getAddress().getAddress().getHostAddress();
plugin.getCore().getPendingLogins().put(ip + username, new Object());
}
private boolean checkNameChange(UUID premiumUUID, PendingConnection connection, String username) {
//user not exists in the db
if (plugin.getConfig().getBoolean("nameChangeCheck")) {
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(premiumUUID);
if (profile != null) {
//uuid exists in the database
plugin.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
requestPremiumLogin(connection, profile, username, false);
return true;
}
}
return false;
}
private void requestPremiumLogin(PendingConnection con, PlayerProfile profile, String username, boolean register) {
con.setOnlineMode(true);
plugin.getSession().put(con, new BungeeLoginSession(username, register, profile));
@Override
public void startCrackedSession(BungeeLoginSource source, PlayerProfile profile, String username) {
plugin.getSession().put(source.getConnection(), new BungeeLoginSession(username, false, profile));
}
}

View File

@@ -1,6 +1,6 @@
package com.github.games647.fastlogin.bungee.tasks;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.BungeeCore;
import com.github.games647.fastlogin.core.PlayerProfile;
import net.md_5.bungee.api.chat.TextComponent;
@@ -8,14 +8,13 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
public class AsyncToggleMessage implements Runnable {
private final FastLoginBungee plugin;
private final BungeeCore core;
private final ProxiedPlayer fromPlayer;
private final String targetPlayer;
private final boolean toPremium;
public AsyncToggleMessage(FastLoginBungee plugin, ProxiedPlayer fromPlayer, String targetPlayer
, boolean toPremium) {
this.plugin = plugin;
public AsyncToggleMessage(BungeeCore core, ProxiedPlayer fromPlayer, String targetPlayer, boolean toPremium) {
this.core = core;
this.fromPlayer = fromPlayer;
this.targetPlayer = targetPlayer;
this.toPremium = toPremium;
@@ -31,27 +30,28 @@ public class AsyncToggleMessage implements Runnable {
}
private void turnOffPremium() {
PlayerProfile playerProfile = plugin.getCore().getStorage().loadProfile(targetPlayer);
if (!playerProfile.isPremium()) {
fromPlayer.sendMessage(TextComponent.fromLegacyText(plugin.getCore().getMessage("not-premium")));
PlayerProfile playerProfile = core.getStorage().loadProfile(targetPlayer);
//existing player is already cracked
if (playerProfile.getUserId() != -1 && !playerProfile.isPremium()) {
fromPlayer.sendMessage(TextComponent.fromLegacyText(core.getMessage("not-premium")));
return;
}
playerProfile.setPremium(false);
playerProfile.setUuid(null);
plugin.getCore().getStorage().save(playerProfile);
fromPlayer.sendMessage(TextComponent.fromLegacyText(plugin.getCore().getMessage("remove-premium")));
core.getStorage().save(playerProfile);
fromPlayer.sendMessage(TextComponent.fromLegacyText(core.getMessage("remove-premium")));
}
private void activatePremium() {
PlayerProfile playerProfile = plugin.getCore().getStorage().loadProfile(targetPlayer);
PlayerProfile playerProfile = core.getStorage().loadProfile(targetPlayer);
if (playerProfile.isPremium()) {
fromPlayer.sendMessage(TextComponent.fromLegacyText(plugin.getCore().getMessage("already-exists")));
fromPlayer.sendMessage(TextComponent.fromLegacyText(core.getMessage("already-exists")));
return;
}
playerProfile.setPremium(true);
plugin.getCore().getStorage().save(playerProfile);
fromPlayer.sendMessage(TextComponent.fromLegacyText(plugin.getCore().getMessage("add-premium")));
core.getStorage().save(playerProfile);
fromPlayer.sendMessage(TextComponent.fromLegacyText(core.getMessage("add-premium")));
}
}

View File

@@ -2,14 +2,15 @@ package com.github.games647.fastlogin.bungee.tasks;
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import java.util.UUID;
import java.util.logging.Level;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
@@ -33,7 +34,7 @@ public class ForceLoginTask implements Runnable {
BungeeLoginSession session = plugin.getSession().get(pendingConnection);
if (session == null || !player.isConnected()) {
plugin.getLogger().log(Level.FINE, "Invalid session player {0} proparly left the server", player);
plugin.getLogger().log(Level.FINE, "Invalid session player {0} propaly left the server", player);
return;
}
@@ -43,7 +44,15 @@ public class ForceLoginTask implements Runnable {
if (pendingConnection.isOnlineMode()) {
boolean autoRegister = session.needsRegistration();
BungeeAuthPlugin authPlugin = plugin.getBungeeAuthPlugin();
//2fa authentication - no need to send bukkit force login notification and so we also don't need
// to wait for a response -> save immediatly
if (!plugin.getConfig().getBoolean("autoLogin")) {
playerProfile.setPremium(true);
plugin.getCore().getStorage().save(playerProfile);
session.setAlreadySaved(true);
}
AuthPlugin<ProxiedPlayer> authPlugin = plugin.getCore().getAuthPluginHook();
if (authPlugin == null) {
//save will happen on success message from bukkit
sendBukkitLoginNotification(autoRegister);
@@ -65,26 +74,39 @@ public class ForceLoginTask implements Runnable {
}
}
private void forceRegister(BungeeLoginSession session, BungeeAuthPlugin authPlugin) {
String password = plugin.generateStringPassword();
if (session.isAlreadySaved() || authPlugin.forceRegister(player, password)) {
private void forceRegister(BungeeLoginSession session, AuthPlugin<ProxiedPlayer> authPlugin) {
if (session.isAlreadyLogged()) {
sendBukkitLoginNotification(true);
return;
}
session.setAlreadyLogged(true);
String password = plugin.getCore().getPasswordGenerator().getRandomPassword(player);
if (authPlugin.forceRegister(player, password)) {
//save will happen on success message from bukkit
sendBukkitLoginNotification(true);
String message = plugin.getCore().getMessage("auto-register");
if (message != null) {
message = message.replace("%password", password);
player.sendMessage(message);
player.sendMessage(TextComponent.fromLegacyText(message));
}
}
}
private void forceLogin(BungeeLoginSession session, BungeeAuthPlugin authPlugin) {
if (session.isAlreadySaved() || authPlugin.forceLogin(player)) {
private void forceLogin(BungeeLoginSession session, AuthPlugin<ProxiedPlayer> authPlugin) {
if (session.isAlreadyLogged()) {
sendBukkitLoginNotification(false);
return;
}
session.setAlreadyLogged(true);
if (authPlugin.forceLogin(player)) {
//save will happen on success message from bukkit
sendBukkitLoginNotification(false);
String message = plugin.getCore().getMessage("auto-login");
if (message != null) {
player.sendMessage(message);
player.sendMessage(TextComponent.fromLegacyText(message));
}
}
}

View File

@@ -8,7 +8,7 @@ main: ${project.groupId}.${project.artifactId}.${project.name}
version: ${project.version}
author: games647, http://github.com/games647/FastLogin/graphs/contributors
softdepends:
softdepend:
# BungeeCord auth plugins
- BungeeAuth

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.7.1</version>
<version>1.9</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -19,7 +19,7 @@
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.4.7</version>
<version>2.5.0</version>
</dependency>
<!--Logging framework implements slf4j which is required by hikari-->
@@ -28,5 +28,12 @@
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>10.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,5 +1,6 @@
package com.github.games647.fastlogin.core;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
@@ -8,6 +9,8 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Calendar;
import java.util.Locale;
import java.util.UUID;
import java.util.logging.Level;
@@ -15,10 +18,13 @@ public class AuthStorage {
private static final String PREMIUM_TABLE = "premium";
private final FastLoginCore core;
private final FastLoginCore<?> core;
private final HikariDataSource dataSource;
public AuthStorage(FastLoginCore core, String driver, String host, int port, String databasePath
//a try to fix https://www.spigotmc.org/threads/fastlogin.101192/page-26#post-1874647
private final Calendar calendar = Calendar.getInstance(Locale.US);
public AuthStorage(FastLoginCore<?> core, String driver, String host, int port, String databasePath
, String user, String pass) {
this.core = core;
@@ -42,6 +48,10 @@ public class AuthStorage {
this.dataSource = new HikariDataSource(databaseConfig);
}
public HikariDataSource getDataSource() {
return dataSource;
}
public void createTables() throws SQLException {
Connection con = null;
Statement createStmt = null;
@@ -85,13 +95,13 @@ public class AuthStorage {
createStmt.executeUpdate("ALTER TABLE premium DROP INDEX UUID");
}
} catch (SQLException sqlEx) {
core.getLogger().log(Level.FINE, "Error dropping unique uuid index", sqlEx);
//silent - we already migrated
}
try {
createStmt.executeUpdate("CREATE INDEX uuid_idx on premium (UUID)");
} catch (SQLException sqlEx) {
core.getLogger().log(Level.FINE, "Error creating uuid index", sqlEx);
//silent - we already migrated
}
} finally {
closeQuietly(con);
@@ -112,17 +122,11 @@ public class AuthStorage {
if (resultSet.next()) {
long userId = resultSet.getInt(1);
String unparsedUUID = resultSet.getString(2);
UUID uuid;
if (unparsedUUID == null) {
uuid = null;
} else {
uuid = FastLoginCore.parseId(unparsedUUID);
}
UUID uuid = FastLoginCore.parseId(resultSet.getString(2));
boolean premium = resultSet.getBoolean(4);
String lastIp = resultSet.getString(5);
long lastLogin = resultSet.getTimestamp(6).getTime();
long lastLogin = resultSet.getTimestamp(6, calendar).getTime();
PlayerProfile playerProfile = new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
return playerProfile;
} else {
@@ -156,7 +160,7 @@ public class AuthStorage {
String name = resultSet.getString(3);
boolean premium = resultSet.getBoolean(4);
String lastIp = resultSet.getString(5);
long lastLogin = resultSet.getTimestamp(6).getTime();
long lastLogin = resultSet.getTimestamp(6, calendar).getTime();
PlayerProfile playerProfile = new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
return playerProfile;
}
@@ -183,24 +187,6 @@ public class AuthStorage {
UUID uuid = playerProfile.getUuid();
if (playerProfile.getUserId() == -1) {
if (uuid != null) {
//User was authenticated with a premium authentication, so it's possible that the player is premium
updateStmt = con.prepareStatement("UPDATE " + PREMIUM_TABLE
+ " SET NAME=?, LastIp=?, LastLogin=CURRENT_TIMESTAMP"
+ " WHERE UUID=?");
updateStmt.setString(1, playerProfile.getPlayerName());
updateStmt.setString(2, playerProfile.getLastIp());
updateStmt.setString(3, uuid.toString().replace("-", ""));
int affectedRows = updateStmt.executeUpdate();
if (affectedRows > 0) {
//username changed and we updated the existing database record
//so we don't need to run an insert
return true;
}
}
saveStmt = con.prepareStatement("INSERT INTO " + PREMIUM_TABLE
+ " (UUID, Name, Premium, LastIp) VALUES (?, ?, ?, ?) ", Statement.RETURN_GENERATED_KEYS);

View File

@@ -1,12 +1,13 @@
package com.github.games647.fastlogin.core;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLSocketFactory;
@@ -17,13 +18,11 @@ public class BalancedSSLFactory extends SSLSocketFactory {
//in order to be thread-safe
private final List<InetAddress> localAddresses;
private final Object lock = new Object();
private AtomicInteger id;
private int id;
public BalancedSSLFactory(SSLSocketFactory oldFactory, Set<InetAddress> localAddresses) {
public BalancedSSLFactory(SSLSocketFactory oldFactory, Iterable<InetAddress> localAddresses) {
this.oldFactory = oldFactory;
this.localAddresses = new ArrayList<>(localAddresses);
this.localAddresses = ImmutableList.copyOf(localAddresses);
}
@Override
@@ -65,16 +64,7 @@ public class BalancedSSLFactory extends SSLSocketFactory {
}
private InetAddress getNextLocalAddress() {
int next;
synchronized (lock) {
next = id;
id++;
if (next == Integer.MAX_VALUE) {
id = 0;
}
}
int index = next % localAddresses.size();
int index = id.incrementAndGet() % localAddresses.size();
return localAddresses.get(index);
}
}

View File

@@ -1,4 +1,4 @@
package com.github.games647.fastlogin.bukkit;
package com.github.games647.fastlogin.core;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentMap;
@@ -10,14 +10,13 @@ import com.google.common.cache.CacheLoader;
import com.google.common.cache.RemovalListener;
/**
* Represents a Guava CacheBuilder that is compatible with both Guava 10 and 13
* Represents a Guava CacheBuilder that is compatible with both Guava 10 (Minecraft 1.7.X) and 13
*/
public class CompatibleCacheBuilder<K, V> {
private static Method BUILD_METHOD;
private static Method AS_MAP_METHOD;
/**
* Construct a new safe cache builder.
*
@@ -27,10 +26,10 @@ public class CompatibleCacheBuilder<K, V> {
* @return A new cache builder.
*/
public static <K, V> CompatibleCacheBuilder<K, V> newBuilder() {
return new CompatibleCacheBuilder<K, V>();
return new CompatibleCacheBuilder<>();
}
private CacheBuilder<K, V> builder;
private final CacheBuilder<K, V> builder;
@SuppressWarnings("unchecked")
private CompatibleCacheBuilder() {

View File

@@ -1,67 +0,0 @@
package com.github.games647.fastlogin.core;
import java.io.File;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
public abstract class FastLoginCore {
public static UUID parseId(String withoutDashes) {
return UUID.fromString(withoutDashes.substring(0, 8)
+ "-" + withoutDashes.substring(8, 12)
+ "-" + withoutDashes.substring(12, 16)
+ "-" + withoutDashes.substring(16, 20)
+ "-" + withoutDashes.substring(20, 32));
}
protected final Map<String, String> localeMessages = new ConcurrentHashMap<>();
private MojangApiConnector mojangApiConnector;
private AuthStorage storage;
public void setMojangApiConnector(MojangApiConnector mojangApiConnector) {
this.mojangApiConnector = mojangApiConnector;
}
public MojangApiConnector getMojangApiConnector() {
return mojangApiConnector;
}
public AuthStorage getStorage() {
return storage;
}
public abstract File getDataFolder();
public abstract Logger getLogger();
public abstract ThreadFactory getThreadFactory();
public String getMessage(String key) {
return localeMessages.get(key);
}
public abstract void loadMessages();
public abstract void loadConfig();
public boolean setupDatabase(String driver, String host, int port, String database, String user, String password) {
storage = new AuthStorage(this, driver, host, port, database, user, password);
try {
storage.createTables();
return true;
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "Failed to setup database. Disabling plugin...", ex);
return false;
}
}
public void close() {
if (storage != null) {
storage.close();
}
}
}

View File

@@ -4,7 +4,7 @@ import java.util.UUID;
public class PlayerProfile {
private final String playerName;
private String playerName;
private long userId;
@@ -13,8 +13,7 @@ public class PlayerProfile {
private String lastIp;
private long lastLogin;
public PlayerProfile(long userId, UUID uuid, String playerName, boolean premium
, String lastIp, long lastLogin) {
public PlayerProfile(long userId, UUID uuid, String playerName, boolean premium, String lastIp, long lastLogin) {
this.userId = userId;
this.uuid = uuid;
this.playerName = playerName;
@@ -24,18 +23,17 @@ public class PlayerProfile {
}
public PlayerProfile(UUID uuid, String playerName, boolean premium, String lastIp) {
this.userId = -1;
this.uuid = uuid;
this.playerName = playerName;
this.premium = premium;
this.lastIp = lastIp;
this(-1, uuid, playerName, premium, lastIp, System.currentTimeMillis());
}
public String getPlayerName() {
public synchronized String getPlayerName() {
return playerName;
}
public synchronized void setPlayerName(String playerName) {
this.playerName = playerName;
}
public synchronized long getUserId() {
return userId;
}

View File

@@ -0,0 +1,26 @@
package com.github.games647.fastlogin.core;
import java.util.Map;
public class SharedConfig {
private final Map<String, Object> configValues;
public SharedConfig(Map<String, Object> configValues) {
this.configValues = configValues;
}
@SuppressWarnings("unchecked")
public <T> T get(String path, T def) {
Object val = configValues.get(path);
return ( val != null ) ? (T) val : def;
}
public <T> T get(String path) {
return get(path, null);
}
public Map<String, Object> getConfigValues() {
return configValues;
}
}

View File

@@ -0,0 +1,59 @@
package com.github.games647.fastlogin.core.hooks;
/**
* Represents a supporting authentication plugin in BungeeCord and Bukkit/Spigot/... servers
*
* @param <P> either org.bukkit.entity.Player for Bukkit or net.md_5.bungee.api.connection.ProxiedPlayer for BungeeCord
*/
public interface AuthPlugin<P> {
/**
* 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;
}

View File

@@ -0,0 +1,20 @@
package com.github.games647.fastlogin.core.hooks;
import java.util.Random;
public class DefaultPasswordGenerator<P> implements PasswordGenerator<P> {
private static final char[] PASSWORD_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
.toCharArray();
private final Random random = new Random();
@Override
public String getRandomPassword(P player) {
StringBuilder generatedPassword = new StringBuilder(8);
for (int i = 1; i <= 8; i++) {
generatedPassword.append(PASSWORD_CHARACTERS[random.nextInt(PASSWORD_CHARACTERS.length - 1)]);
}
return generatedPassword.toString();
}
}

View File

@@ -0,0 +1,6 @@
package com.github.games647.fastlogin.core.hooks;
public interface PasswordGenerator<P> {
String getRandomPassword(P player);
}

View File

@@ -1,51 +1,73 @@
package com.github.games647.fastlogin.core.importer;
import com.github.games647.fastlogin.core.AuthStorage;
import com.github.games647.fastlogin.core.PlayerProfile;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.UUID;
import javax.sql.DataSource;
public class AutoInImporter extends Importer {
private static final String PLUGIN_NAME = "AutoIn";
private static final String SQLITE_FILE = "plugins/" + PLUGIN_NAME + "/AutoIn_PlayerOptions.db";
private static final String USER_TABLE = "nicknames";
private static final String UUID_TABLE = "uuids";
private static final String SESSION_TABLE = "sessions";
public static String getSQLitePath() {
return SQLITE_FILE;
}
@Override
public int importData(DataSource source, DataSource target, String targetTable) throws SQLException {
Connection con = null;
public int importData(Connection source, DataSource target, AuthStorage storage) throws SQLException {
Statement stmt = null;
ResultSet resultSet = null;
try {
con = source.getConnection();
stmt = con.createStatement();
int importedRows = stmt.executeUpdate("INSERT INTO " + targetTable + " (Name, Premium, LastIp, UUID) SELECT"
+ " name AS Name,"
/* Enable premium authentication only for those who want to be auto logged in, so
they have their cracked protection disabled */
+ " !protection AND premium AS Premium,"
+ " '' AS LastIp,"
/* Remove the dashes - returns null if puuid is null too */
+ " REPLACE(puuid, '-', '') AS UUID"
+ " FROM " + USER_TABLE
/* Get the premium uuid */
stmt = source.createStatement();
resultSet = stmt.executeQuery("SELECT name, protection, premium, puuid FROM " + USER_TABLE
+ " LEFT JOIN " + " ("
/* Prevent duplicates */
+ "SELECT * FROM " + UUID_TABLE + " GROUP BY nickname_id"
+ ") uuids"
+ " ON " + USER_TABLE + ".id = uuids.nickname_id");
/* FastLogin will also make lookups on the uuid column for name changes
the old 1.6.2 version won't check if those user have premium enabled
int rows = 0;
while (resultSet.next()) {
String name = resultSet.getString(1);
boolean protection = resultSet.getBoolean(2);
/* Enable premium authentication only for those who want to be auto logged in,
so they have their cracked protection disabled */
boolean premium = !protection && resultSet.getBoolean(3);
String puuid = resultSet.getString(4);
so it could happen that a premium could steal the account if we don't do this
/* FastLogin will also make lookups on the uuid column for name changes
the old 1.6.2 version won't check if those user have premium enabled
It seems the uuid is saved on autoin too if the player is cracked */
stmt.executeUpdate("UPDATE `premium` SET `UUID`=NULL WHERE PREMIUM=0");
return importedRows;
so it could happen that a premium could steal the account if we don't do this
It seems the uuid is saved on autoin too if the player is cracked */
PlayerProfile profile;
if (premium) {
profile = new PlayerProfile(UUID.fromString(puuid), name, premium, "");
} else {
profile = new PlayerProfile(null, name, premium, "");
}
storage.save(profile);
rows++;
}
return rows;
} finally {
closeQuietly(stmt);
closeQuietly(con);
closeQuietly(resultSet);
}
}
}

View File

@@ -1,8 +1,13 @@
package com.github.games647.fastlogin.core.importer;
import com.github.games647.fastlogin.core.AuthStorage;
import com.github.games647.fastlogin.core.PlayerProfile;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import javax.sql.DataSource;
@@ -11,23 +16,35 @@ public class BPAImporter extends Importer {
private static final String DEFAULT_TABLE_NAME = "users";
@Override
public int importData(DataSource source, DataSource target, String targetTable) throws SQLException {
Connection con = null;
public int importData(Connection source, DataSource target, AuthStorage storage) throws SQLException {
Statement stmt = null;
ResultSet resultSet = null;
try {
con = source.getConnection();
stmt = con.createStatement();
int importedRows = stmt.executeUpdate("INSERT INTO " + targetTable + " SELECT"
+ " nick AS Name,"
+ " NULL AS UUID,"
+ " checked AS Premium,"
+ " lastIP AS LastIp,"
+ " FROM_UNIXTIME(lastJoined * 0.001) AS LastLogin"
+ " FROM " + DEFAULT_TABLE_NAME);
return importedRows;
stmt = source.createStatement();
resultSet = stmt.executeQuery("SELECT "
+ "nick, "
+ "checked, "
+ "lastIP, "
+ "FROM_UNIXTIME(lastJoined * 0.001) AS LastLogin "
+ "FROM " + DEFAULT_TABLE_NAME);
int rows = 0;
while (resultSet.next()) {
String name = resultSet.getString(1);
boolean premium = resultSet.getBoolean(2);
String lastIP = resultSet.getString(3);
Timestamp lastLogin = resultSet.getTimestamp(4);
//uuid doesn't exist here
PlayerProfile profile = new PlayerProfile(null, name, premium, lastIP);
storage.save(profile);
rows++;
}
return rows;
} finally {
closeQuietly(stmt);
closeQuietly(con);
closeQuietly(resultSet);
}
}
}

View File

@@ -1,8 +1,14 @@
package com.github.games647.fastlogin.core.importer;
import com.github.games647.fastlogin.core.AuthStorage;
import com.github.games647.fastlogin.core.PlayerProfile;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.UUID;
import javax.sql.DataSource;
@@ -11,23 +17,42 @@ public class ElDziAuthImporter extends Importer {
private static final String TABLE_NAME = "accounts";
@Override
public int importData(DataSource source, DataSource target, String targetTable) throws SQLException {
Connection con = null;
public int importData(Connection source, DataSource target, AuthStorage storage) throws SQLException {
Statement stmt = null;
ResultSet resultSet = null;
try {
con = source.getConnection();
stmt = con.createStatement();
int importedRows = stmt.executeUpdate("INSERT INTO " + targetTable + " SELECT"
+ " nick AS Name,"
+ " uuid AS UUID,"
+ " premium AS Premium,"
+ " lastIp AS LastIp,"
+ " FROM_UNIXTIME(lastPlayed * 0.001) AS LastLogin"
+ " FROM " + TABLE_NAME);
return importedRows;
stmt = source.createStatement();
resultSet = stmt.executeQuery("SELECT "
+ "nick, "
+ "premium, "
+ "lastIP, "
+ "FROM_UNIXTIME(lastPlayed * 0.001) AS LastLogin "
+ "FROM " + TABLE_NAME);
int rows = 0;
while (resultSet.next()) {
String name = resultSet.getString(1);
boolean premium = resultSet.getBoolean(2);
String lastIP = resultSet.getString(3);
Timestamp lastLogin = resultSet.getTimestamp(4);
String uuid = resultSet.getString(5);
PlayerProfile profile;
if (premium) {
profile = new PlayerProfile(UUID.fromString(uuid), name, premium, lastIP);
} else {
profile = new PlayerProfile(null, name, premium, "");
}
storage.save(profile);
rows++;
}
return rows;
} finally {
closeQuietly(stmt);
closeQuietly(con);
closeQuietly(resultSet);
}
}
}

View File

@@ -0,0 +1,20 @@
package com.github.games647.fastlogin.core.importer;
public enum ImportPlugin {
AUTO_IN(AutoInImporter.class),
BPA(BPAImporter.class),
ELDZI(ElDziAuthImporter.class);
private final Class<? extends Importer> importerClass;
ImportPlugin(Class<? extends Importer> importer) {
this.importerClass = importer;
}
public Class<? extends Importer> getImporter() {
return importerClass;
}
}

View File

@@ -1,12 +1,13 @@
package com.github.games647.fastlogin.core.importer;
import com.github.games647.fastlogin.core.AuthStorage;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
public abstract class Importer {
public abstract int importData(DataSource source, DataSource target, String targetTable) throws SQLException;
public abstract int importData(Connection source, DataSource target, AuthStorage storage) throws SQLException;
protected void closeQuietly(AutoCloseable closeable) {
if (closeable != null) {

View File

@@ -0,0 +1,198 @@
package com.github.games647.fastlogin.core.shared;
import com.github.games647.fastlogin.core.AuthStorage;
import com.github.games647.fastlogin.core.CompatibleCacheBuilder;
import com.github.games647.fastlogin.core.SharedConfig;
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.importer.AutoInImporter;
import com.github.games647.fastlogin.core.importer.ImportPlugin;
import com.github.games647.fastlogin.core.importer.Importer;
import com.google.common.cache.CacheLoader;
import com.google.common.collect.Sets;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @param <P> Player class
*/
public abstract class FastLoginCore<P> {
public static <K, V> ConcurrentMap<K, V> buildCache(int expireAfterWrite, int maxSize) {
CompatibleCacheBuilder<Object, Object> builder = CompatibleCacheBuilder.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 UUID parseId(String withoutDashes) {
if (withoutDashes == null) {
return null;
}
return UUID.fromString(withoutDashes.substring(0, 8)
+ "-" + withoutDashes.substring(8, 12)
+ "-" + withoutDashes.substring(12, 16)
+ "-" + withoutDashes.substring(16, 20)
+ "-" + withoutDashes.substring(20, 32));
}
protected final Map<String, String> localeMessages = new ConcurrentHashMap<>();
private final ConcurrentMap<String, Object> pendingLogins = FastLoginCore.buildCache(5, 0);
private final Set<UUID> pendingConfirms = Sets.newHashSet();
private final SharedConfig sharedConfig;
private MojangApiConnector apiConnector;
private AuthStorage storage;
private PasswordGenerator<P> passwordGenerator = new DefaultPasswordGenerator<>();
private AuthPlugin<P> authPlugin;
public FastLoginCore(Map<String, Object> config) {
this.sharedConfig = new SharedConfig(config);
}
public void setApiConnector() {
List<String> ipAddresses = sharedConfig.get("ip-addresses");
int requestLimit = sharedConfig.get("mojang-request-limit");
this.apiConnector = makeApiConnector(getLogger(), ipAddresses, requestLimit);
}
public MojangApiConnector getApiConnector() {
return apiConnector;
}
public AuthStorage getStorage() {
return storage;
}
public abstract File getDataFolder();
public abstract Logger getLogger();
public abstract ThreadFactory getThreadFactory();
public String getMessage(String key) {
return localeMessages.get(key);
}
public abstract void loadMessages();
public abstract MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests);
public boolean setupDatabase() {
String driver = sharedConfig.get("driver");
String host = sharedConfig.get("host", "");
int port = sharedConfig.get("port", 3306);
String database = sharedConfig.get("database");
String user = sharedConfig.get("username", "");
String password = sharedConfig.get("password", "");
storage = new AuthStorage(this, driver, host, port, database, user, password);
try {
storage.createTables();
return true;
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "Failed to setup database. Disabling plugin...", ex);
return false;
}
}
public boolean importDatabase(ImportPlugin plugin, boolean sqlite, AuthStorage storage, String host, String database
, String username, String pass) {
if (sqlite && (plugin == ImportPlugin.BPA || plugin == ImportPlugin.ELDZI)) {
throw new IllegalArgumentException("These plugins doesn't support flat file databases");
}
Importer importer;
try {
importer = plugin.getImporter().newInstance();
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "Couldn't not setup importer class", ex);
return false;
}
try {
if (sqlite && plugin == ImportPlugin.AUTO_IN) {
//load sqlite driver
Class.forName("org.sqlite.JDBC");
String jdbcUrl = "jdbc:sqlite:" + AutoInImporter.getSQLitePath();
Connection con = DriverManager.getConnection(jdbcUrl);
importer.importData(con, storage.getDataSource(), storage);
return true;
} else {
Class.forName("com.mysql.jdbc.Driver");
String jdbcUrl = "jdbc:mysql://" + host + "/" + database;
Connection con = DriverManager.getConnection(jdbcUrl, username, pass);
importer.importData(con, storage.getDataSource(), storage);
return true;
}
} catch (ClassNotFoundException ex) {
getLogger().log(Level.SEVERE, "Cannot find SQL driver. Do you removed it?", ex);
} catch (SQLException ex) {
getLogger().log(Level.SEVERE, "Couldn't import data. Aborting...", ex);
}
return false;
}
public SharedConfig getConfig() {
return sharedConfig;
}
public PasswordGenerator<P> getPasswordGenerator() {
return passwordGenerator;
}
public void setPasswordGenerator(PasswordGenerator<P> passwordGenerator) {
this.passwordGenerator = passwordGenerator;
}
public ConcurrentMap<String, Object> getPendingLogins() {
return pendingLogins;
}
public Set<UUID> getPendingConfirms() {
return pendingConfirms;
}
public AuthPlugin<P> getAuthPluginHook() {
return authPlugin;
}
public void setAuthPluginHook(AuthPlugin<P> authPlugin) {
this.authPlugin = authPlugin;
}
public void close() {
if (storage != null) {
storage.close();
}
}
}

View File

@@ -0,0 +1,98 @@
package com.github.games647.fastlogin.core.shared;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.core.SharedConfig;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import java.util.UUID;
import java.util.logging.Level;
public abstract class JoinManagement<T, S extends LoginSource> {
protected final FastLoginCore<T> core;
protected final AuthPlugin<T> authHook;
public JoinManagement(FastLoginCore<T> core, AuthPlugin<T> authHook) {
this.core = core;
this.authHook = authHook;
}
public void onLogin(String username, S source) {
PlayerProfile profile = core.getStorage().loadProfile(username);
if (profile == null) {
return;
}
SharedConfig config = core.getConfig();
String ip = source.getAddress().getAddress().getHostAddress();
try {
if (profile.getUserId() == -1) {
if (core.getPendingLogins().containsKey(ip + username) && config.get("secondAttemptCracked", false)) {
core.getLogger().log(Level.INFO, "Second attempt login -> cracked {0}", username);
//first login request failed so make a cracked session
startCrackedSession(source, profile, username);
return;
}
UUID premiumUUID = null;
if (config.get("nameChangeCheck", false) || config.get("autoRegister", false)) {
core.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
premiumUUID = core.getApiConnector().getPremiumUUID(username);
}
if (premiumUUID == null
|| (!checkNameChange(source, username, premiumUUID)
&& !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);
}
} else if (profile.isPremium()) {
requestPremiumLogin(source, profile, username, true);
} else {
startCrackedSession(source, profile, username);
}
} catch (Exception ex) {
core.getLogger().log(Level.SEVERE, "Failed to check premium state", ex);
}
}
private boolean checkPremiumName(S source, String username, PlayerProfile profile) throws Exception {
if (core.getConfig().get("autoRegister", false) && (authHook == null || !authHook.isRegistered(username))) {
requestPremiumLogin(source, profile, username, false);
return true;
}
return false;
}
private boolean checkNameChange(S source, String username, UUID premiumUUID) {
//user not exists in the db
if (core.getConfig().get("nameChangeCheck", false)) {
PlayerProfile profile = core.getStorage().loadProfile(premiumUUID);
if (profile != null) {
//uuid exists in the database
core.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
//update the username to the new one in the database
profile.setPlayerName(username);
requestPremiumLogin(source, profile, username, false);
return true;
}
}
return false;
}
public abstract void requestPremiumLogin(S source, PlayerProfile profile, String username, boolean registered);
public abstract void startCrackedSession(S source, PlayerProfile profile, String username);
}

View File

@@ -1,9 +1,16 @@
package com.github.games647.fastlogin.core;
package com.github.games647.fastlogin.core.shared;
public class LoginSession {
import com.github.games647.fastlogin.core.PlayerProfile;
import java.util.UUID;
public abstract class LoginSession {
private final String username;
private final PlayerProfile profile;
private UUID uuid;
protected boolean registered;
public LoginSession(String username, boolean registered, PlayerProfile profile) {
@@ -28,4 +35,22 @@ public class LoginSession {
public PlayerProfile 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;
}
}

View File

@@ -0,0 +1,12 @@
package com.github.games647.fastlogin.core.shared;
import java.net.InetSocketAddress;
public interface LoginSource {
void setOnlineMode() throws Exception;
void kick(String message) throws Exception;
InetSocketAddress getAddress();
}

View File

@@ -1,13 +1,17 @@
package com.github.games647.fastlogin.core;
package com.github.games647.fastlogin.core.shared;
import com.github.games647.fastlogin.core.BalancedSSLFactory;
import com.google.common.collect.Sets;
import com.google.common.io.CharStreams;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -36,17 +40,15 @@ public abstract class MojangApiConnector {
//compile the pattern only on plugin enable -> and this have to be threadsafe
private final Pattern playernameMatcher = Pattern.compile(VALID_PLAYERNAME);
private final ConcurrentMap<Object, Object> requests;
private final ConcurrentMap<Object, Object> requests = FastLoginCore.buildCache(10, -1);
private final BalancedSSLFactory sslFactory;
private final int rateLimit;
private long lastRateLimit;
protected final Logger logger;
public MojangApiConnector(ConcurrentMap<Object, Object> requests, Logger logger, List<String> localAddresses
, int rateLimit) {
public MojangApiConnector(Logger logger, List<String> localAddresses, int rateLimit) {
this.logger = logger;
this.requests = requests;
if (rateLimit > 600) {
this.rateLimit = 600;
@@ -57,12 +59,12 @@ public abstract class MojangApiConnector {
if (localAddresses.isEmpty()) {
this.sslFactory = null;
} else {
Set<InetAddress> addresses = new HashSet<>();
Set<InetAddress> addresses = Sets.newHashSet();
for (String localAddress : localAddresses) {
try {
InetAddress address = InetAddress.getByName(localAddress);
if (!address.isAnyLocalAddress()) {
logger.log(Level.WARNING, "Submitted IP-Address is not local", address);
logger.log(Level.WARNING, "Submitted IP-Address is not local {0}", address);
continue;
}
@@ -98,8 +100,8 @@ public abstract class MojangApiConnector {
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line = reader.readLine();
if (line != null && !line.equals("null")) {
return getUUIDFromJson(line);
if (!line.equals("null")) {
return FastLoginCore.parseId(getUUIDFromJson(line));
}
} else if (connection.getResponseCode() == RATE_LIMIT_CODE) {
logger.info("RATE_LIMIT REACHED - TRYING THIRD-PARTY API");
@@ -118,24 +120,16 @@ public abstract class MojangApiConnector {
public UUID getUUIDFromAPI(String playerName) {
try {
HttpURLConnection httpConnection = (HttpURLConnection) new URL(MCAPI_UUID_URL + playerName).openConnection();
httpConnection.addRequestProperty("Content-Type", "application/json");
httpConnection.setRequestProperty("User-Agent", USER_AGENT);
HttpURLConnection httpConnection = getConnection(MCAPI_UUID_URL + playerName);
if (httpConnection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
//cracked
return null;
}
BufferedReader reader = new BufferedReader(new InputStreamReader(httpConnection.getInputStream()));
StringBuilder inputBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
inputBuilder.append(line);
}
String input = inputBuilder.toString();
return getUUIDFromJson(input);
Reader reader = new InputStreamReader(httpConnection.getInputStream());
String json = CharStreams.toString(reader);
return FastLoginCore.parseId(getUUIDFromJson(json));
} catch (IOException iOException) {
logger.log(Level.SEVERE, "Tried converting name->uuid from third-party api", iOException);
}
@@ -143,9 +137,9 @@ public abstract class MojangApiConnector {
return null;
}
public abstract boolean hasJoinedServer(Object session, String serverId);
public abstract boolean hasJoinedServer(LoginSession session, String serverId);
protected abstract UUID getUUIDFromJson(String json);
protected abstract String getUUIDFromJson(String json);
protected HttpsURLConnection getConnection(String url) throws IOException {
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();

View File

@@ -21,6 +21,21 @@
# For more information: https://github.com/games647/FastLogin#why-do-players-have-to-invoke-a-command
autoRegister: 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 whitelist.
# 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 players changes their usernames, they will still have
# the same playerdata (inventory, permissions, ...)
@@ -129,6 +144,13 @@ ip-addresses: []
# If you want to join the discussion visit this: https://github.com/games647/FastLogin/issues/27#issuecomment-226954350
mojang-request-limit: 600
# 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
# Database configuration
# Recommened is the use of MariaDB (a better version of MySQL)

View File

@@ -21,9 +21,15 @@
# ========= Shared (BungeeCord and Bukkit) ============
# Switch mode is activated and a new (non-whitelist) cracked player tries to join
switch-kick-message: '&4Only paid minecraft whitelisted accounts are allowed to join this server'
# Player activated premium logins in order to skip offline authentication
add-premium: '&2Added to the list of premium players'
# Player activated premium logins in order to skip offline authentication
add-premium-other: '&2Player has been added to the premium list'
# Player is already set be a paid account
already-exists: '&4You are already on the premium list'

15
pom.xml
View File

@@ -8,7 +8,7 @@
<packaging>pom</packaging>
<name>FastLogin</name>
<version>1.7.1</version>
<version>1.9</version>
<inceptionYear>2015</inceptionYear>
<url>https://www.spigotmc.org/resources/fastlogin.14153/</url>
<description>
@@ -50,21 +50,12 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.8</source>
<target>1.8</target>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<outputDirectory>${outputDir}</outputDirectory>
</configuration>
</plugin>
</plugins>
<resources>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.7.1</version>
<version>1.9</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -43,6 +43,15 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<outputDirectory>${outputDir}</outputDirectory>
</configuration>
</plugin>
</plugins>
</build>