Compare commits

..

50 Commits
1.9 ... 1.10

Author SHA1 Message Date
games647
484855724b Add ip parameter to verify a player doesn't use an authentication proxy.
This doesn't prevent proxy connections in general, but it verifies that
the same IP that is used for connecting to the Minecraft server is also
used for authenticating against the Mojang servers.

This happens if someone uses McLeaks. They use an authentication proxy
in order to hide and control the credentials behind those leaked or
donated accounts. So a user of that service joins the server using
a direct connection, but asks the McLeaks servers to send a relevant
request to the Mojang session-servers in order to pass the premium
verification process.
2017-08-25 13:20:55 +02:00
games647
4ea7968366 Remove Importer to prepare for code refactor 2017-08-24 18:50:37 +02:00
games647
44a47bc97f Set default value for proxies 2017-08-20 21:40:37 +02:00
games647
82cb25f809 Output more informational messages by default 2017-08-19 21:53:07 +02:00
games647
551441cdc4 Add HTTP-proxies support 2017-08-18 16:09:59 +02:00
games647
22a56862b0 Remove mcapi.ca section and fix config typos 2017-08-16 17:18:58 +02:00
games647
edf5933e07 Set the fake offline UUID on lowest priority (-> as soon as possible)
Then every plugin listening on priority level higher than lowest can see that fake UUID

This also fixes race conditions for plugins listening on the same priority as FastLogin before (->low)
(Fixes #167)
2017-08-01 10:29:58 +02:00
games647
c6da04de70 Fix listening for login start packets if ProtocolLib is installed
Another call on ProtocolLib's types removes all previous listening types

Fixes #163
2017-07-25 13:18:08 +02:00
games647
0459b0a5a1 Remove bungee chatcolor for Bukkit to support KCauldron 2017-07-22 08:35:32 +02:00
games647
033333e35c Minor cleanup using inspections + Https
* Use https for maven repositories if possible
* Fix typos
* Merge ProtocolLib listeners into one class
* Upgrade maven plugins and dependencies
2017-07-22 08:27:55 +02:00
games647
6595dc6ac0 Increase hook delay to let ProtocolLib inject the listener 2017-06-30 17:37:57 +02:00
games647
ea44002e91 Update dependencies and format imports 2017-06-30 17:23:46 +02:00
games647
131de8404c Add support for new authme API 2017-06-12 17:26:46 +02:00
games647
fbdd8ffc35 Choose player name casing based on client request.
Since BungeeCord commit 5bc189fbb7e8ca19984544af78a083024404fb2a the name casing is based on
the exact name saved at Mojang. This means it could have breaking effects on FastLogin, because
it performs case-sensitive checks against the database. To provide backwards compatibility with
old data we restore the old implementation access for FastLogin.

Thanks to @Maxetto for pointing this out. This commit basically reverts:
059c3f346e
2017-06-07 21:09:00 +02:00
games647
7db8c78975 Drop support for old authme API 2017-06-04 15:52:01 +02:00
games647
b102f06f8e Update ProtocolLib to fix building 2017-05-27 11:24:43 +02:00
games647
a79e18445a Fix building because the bungee proxy repo is down [ci skip] 2017-05-19 12:01:02 +02:00
games647
cf1a0c1bef Remove ebean util usage to make it compatible with 1.12 2017-05-14 17:11:10 +02:00
games647
059c3f346e Lowercase name inside pendingconnection for comparisons against the database 2017-05-10 17:06:25 +02:00
Leo G. ~ Leoko
47db2c7858 Fixed AuthHook (#144)
* Fixed AuthHook

The setServerStarted()-Method is now also called if an extern AuthHook
hooks into FastLogin via the API

* Simplified if-Statement
2017-04-19 14:39:27 +02:00
games647
5bb8640d78 Do not try to hook into a plugin if auth plugin hook is already set using the FastLogin API 2017-04-17 15:22:09 +02:00
games647
881b2ec7bc Fix changelog markdown syntax 2017-04-15 09:42:17 +02:00
games647
194c67cd6f Fix markdown syntax 2017-04-05 09:24:41 +02:00
games647
863607c9a4 Add optional useSSL config option 2017-02-23 09:16:11 +01:00
games647
f37cc0a0db Add commit id to the version 2017-02-14 14:01:57 +01:00
games647
70a81bfcdf Correctly wait for BungeeAuth loading by using the correct depend tag (Fixes #119) 2017-02-10 19:06:57 +01:00
games647
b8d029d6da Remove third party API 2017-02-04 14:09:38 +01:00
games647
c47dd1df80 Fix FileNotFoundEx if the bungee config doesn't exist 2017-01-28 16:38:48 +01:00
games647
4d5b1787b1 Migrate to Java 7 NIO files 2017-01-26 09:52:45 +01:00
games647
8c764220bd Fix duplicate premium username message 2017-01-21 18:02:45 +01:00
games647
9af076b4c4 Fix premium username logging message at the wrong place 2017-01-09 17:57:50 +01:00
games647
22aa9287e9 Fix NoClassDef errors if the optional PlaceholderAPI is not available (Fixes #108) 2017-01-07 18:42:10 +01:00
games647
f08daa9b72 Update bungee-proxy maven repository 2017-01-06 13:00:17 +01:00
games647
bc53743c6b Add placeholder variables 2017-01-06 12:54:02 +01:00
games647
a430a079c9 Do no print auto login message on authme session reuse (Related #101) 2016-12-23 22:12:55 +01:00
games647
f3ac6090f1 Fix bungee online check (Fixes #101) 2016-12-23 10:01:38 +01:00
games647
5ca9b9c59a Add note about firewalling your spigot server if you use BungeeCord 2016-12-22 09:13:58 +01:00
games647
b886d1501f Update LoginSecurity to make it buildable 2016-12-16 15:56:30 +01:00
games647
0082cc6536 Use static builder to make it independent from ProtocolLib without throwing NoClassDefFoundError 2016-12-16 15:49:40 +01:00
games647
7f96d55084 Convert config values to string if casting fails 2016-11-26 13:27:39 +01:00
games647
3851d539f8 Workaround injector class is package private in older versions of ProtocolLib (Fixes #94) 2016-11-26 11:33:15 +01:00
games647
a25d97879f Fail safetly if there session was started (prevents duplicate errors) 2016-11-26 10:06:27 +01:00
games647
41abffdb08 Fix Spigot console command invocation sends result to ingame players 2016-10-20 14:06:18 +02:00
games647
e69eb70377 Update BungeeAuth dependency and use the new API 2016-10-05 10:06:02 +02:00
games647
e924b7a2fa Automatically register players who are not known to the auth plugin
Fixes #85
2016-10-03 13:46:33 +02:00
games647
157ca04691 Fix timestamp parsing in newer versions of SQLite 2016-09-23 12:26:18 +02:00
games647
ae3e03405d No duplicate login's like auth plugins auto logins if it's the same ip 2016-09-23 10:42:25 +02:00
games647
bebb04bdea Share the same force login mangement for less duplicate code 2016-09-22 10:56:31 +02:00
games647
91f41c55de Finally set a value to the API column 2016-09-21 13:24:26 +02:00
games647
1acc825f81 Remove deprecated API methods 2016-09-21 13:22:48 +02:00
68 changed files with 1190 additions and 1708 deletions

View File

@@ -1,6 +1,6 @@
# Use https://travis-ci.org/ for automatic tests
# speed up testing http://blog.travis-ci.com/2014-12-17-faster-builds-with-container-based-infrastructure/
# speed up testing https://blog.travis-ci.com/2014-12-17-faster-builds-with-container-based-infrastructure/
sudo: false
# This is a java project

View File

@@ -1,4 +1,22 @@
######1.9
### 1.10
* Add support for HTTP proxies
* Set the fake offline UUID on lowest priority (-> as soon as possible)
* Remove bungee chatcolor for Bukkit to support KCauldron
* Minor cleanup using inspections + Https
* Increase hook delay to let ProtocolLib inject the listener
* Drop support for old AuthMe API + Add support for new AuthMe API
* Remove ebean util usage to make it compatible with 1.12
* Do not try to hook into a plugin if auth plugin hook is already set using the FastLogin API
* Automatically register accounts if they are not in the auth plugin database but in the FastLogin database
* Update BungeeAuth dependency and use the new API. Please update your plugin if you still use the old one.
* Remove deprecated API methods from the last version
* Finally set a value to the API column
* No duplicate session login
* Fix timestamp parsing in newer versions of SQLite
* Fix Spigot console command invocation sends result to in game players
### 1.9
* Added second attempt login -> cracked login
* Added cracked whitelist (switch-mode -> switching to online-mode from offlinemode)
@@ -17,7 +35,7 @@
* 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
### 1.8
* Added autoIn importer
* Added BFA importer
@@ -26,12 +44,12 @@
* Fix ProtocolSupport BungeeCord
* Fix duplicate logins for BungeeAuth users
######1.7.1
### 1.7.1
* Fix BungeeCord autoRegister (Fixes #46)
* Fix protocollsupport autoregister
######1.7
### 1.7
* Added support for making requests to Mojang from different IPv4 addresses
* Added us.mcapi.com as third-party APIs to workaround rate-limits
@@ -44,15 +62,15 @@
* Fix setting skin on Cauldron
* Fix saving on name change
######1.6.2
### 1.6.2
* Fixed support for new LoginSecurity version
######1.6.1
### 1.6.1
* Fix message typo in BungeeCord which created a NPE if premium-warning is activated
######1.6
### 1.6
* Add a warning message if the user tries to invoke the premium command
* Added missing translation if the server isn't fully started
@@ -63,34 +81,34 @@
* Fixed cracked command not working on BungeeCord
* Fix error if forward skins is disabled
######1.5.2
### 1.5.2
* Fixed BungeeCord force logins if there is a lobby server
* Removed cache expire in BungeeCord
* Applies skin earlier to make it visible for other plugins listening on login events
######1.5.1
### 1.5.1
* Fixed BungeeCord support by correctly saving the proxy ids
######1.5
### 1.5
* Added localization
* Fixed NPE on premium name check if it's pure cracked player
* Fixed NPE in BungeeCord on cracked login for existing players
* Fixed saving of existing cracked players
######1.4
### 1.4
* Added Bungee setAuthPlugin method
* Added nameChangeCheck
* Multiple BungeeCord support
######1.3.1
### 1.3.1
* Prevent thread create violation in BungeeCord
######1.3
### 1.3
* Added support for AuthMe 3.X
* Fixed premium logins if the server is not fully started
@@ -99,11 +117,11 @@
* Fixed 1.7 Minecraft support by removing guava 11+ only features -> Cauldron support
* Fixed BungeeCord support in Cauldron
######1.2.1
### 1.2.1
* Fix premium status change notification message on BungeeCord
######1.2
### 1.2
* Fix race condition in BungeeCord
* Fix dead lock in xAuth
@@ -111,20 +129,20 @@
* Added API methods for plugins to set their own auth plugin hook
=> Added support for AdvancedLogin
######1.1
### 1.1
* Make the configuration options also work under BungeeCord (premiumUUID, forwardSkin)
* Catch configuration loading exception if it's not spigot build
* Fix config loading for older Spigot builds
######1.0
### 1.0
* Massive refactor to handle errors on force actions safely
* force Methods now runs async too
* force methods now returns a boolean to reflect if the method was successful
* isRegistered method should now throw an exception if the plugin was unable to query the requested data
######0.8
### 0.8
* Fixed BungeeCord support for the Bukkit module
* Added database storage to save the premium state
@@ -132,7 +150,7 @@
* Fixed issues with host lookup from hosts file (Thanks to @NorbiPeti)
* Remove handshake listener because it creates errors on some systems
######0.7
### 0.7
* Added BungeeAuth support
* Added /premium [player] command with optional player parameter
@@ -145,14 +163,14 @@
* Fixed BungeeCord support
* Changed config option autologin to autoregister to clarify the usage
######0.6
### 0.6
* Fixed 1.9 bugs
* Added UltraAuth support
######0.5
### 0.5
* Added unpremium command
* Added cracked command
* Added autologin - See config
* Added config
* Added isRegistered API method
@@ -162,34 +180,34 @@
* Fixed premium name check to protocolsupport
* Improved permissions management
######0.4
### 0.4
* Added forward premium skin
* Added plugin support for protocolsupport
######0.3.2
### 0.3.2
* Run packet readers in a different thread (separated from the Netty I/O Thread)
-> Improves performance
* Fixed Plugin disable if the server is in online mode but have to be in offline mode
######0.3.1
### 0.3.1
* Improved BungeeCord security
#####0.3
### 0.3
* Added BungeeCord support
* Decrease timeout checks in order to fail faster on connection problems
* Code style improvements
######0.2.4
### 0.2.4
* Fixed NPE on invalid sessions
* Improved security by generating a randomized serverId
* Removed /premium [player] because it's safer for premium players who join without registration
######0.2.3
### 0.2.3
* Remove useless AuthMe forcelogin code
* Send a kick message to the client instead of just "Disconnect"
@@ -197,15 +215,15 @@
* Fix thread safety for fake start packets (Bukkit.getOfflinePlayer doesn't look like to be thread-safe)
* Added more documentation
######0.2.2
### 0.2.2
* Compile project with Java 7 :(
######0.2.1
### 0.2.1
* A couple of security fixes (premium players cannot longer steal the account of a cracked account)
* Added a /premium command to mark you as premium player
#####0.2
### 0.2
* Added support for CrazyLogin and LoginSecurity
* Now minecraft version independent
@@ -214,5 +232,5 @@
* More state validation
* Added better error handling
#####0.1
### 0.1
* First release

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2015
Copyright (c) 2015-17
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,12 +1,9 @@
# FastLogin
[![Build Status](https://travis-ci.org/games647/FastLogin.svg?branch=master)](https://travis-ci.org/games647/FastLogin)
[![Donate Button](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8ZBULMAPN7MZC)
Checks if a Minecraft player has a paid account (premium). If so, they can skip offline authentication (auth plugins).
So they don't need to enter passwords. This is also called auto login (auto-login).
###Features:
### Features:
* Detect paid accounts from others
* Automatically login paid accounts (premium)
@@ -28,55 +25,55 @@ So they don't need to enter passwords. This is also called auto login (auto-logi
***
###Commands:
### 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:
### 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/)
### Requirements:
* Plugin: [ProtocolLib](https://www.spigotmc.org/resources/protocollib.1997/) or [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/)
* Tested Bukkit/[Spigot](https://www.spigotmc.org) 1.9 (could also work with other versions)
* Java 7+
* Run Spigot and/or BungeeCord/Waterfall in offline mode (see server.properties or config.yml)
* An auth plugin. Supported plugins
####Bukkit/Spigot/PaperSpigot
#### Bukkit/Spigot/Paper
* [AuthMe (both 5.X and 3.X)](http://dev.bukkit.org/bukkit-plugins/authme-reloaded/)
* [xAuth](http://dev.bukkit.org/bukkit-plugins/xauth/)
* [AuthMe (both 5.X and 3.X)](https://dev.bukkit.org/bukkit-plugins/authme-reloaded/)
* [xAuth](https://dev.bukkit.org/bukkit-plugins/xauth/)
* [LogIt](https://github.com/XziomekX/LogIt)
* [AdvancedLogin (Paid)](https://www.spigotmc.org/resources/advancedlogin.10510/)
* [CrazyLogin](http://dev.bukkit.org/bukkit-plugins/crazylogin/)
* [LoginSecurity](http://dev.bukkit.org/bukkit-plugins/loginsecurity/)
* [RoyalAuth](http://dev.bukkit.org/bukkit-plugins/royalauth/)
* [UltraAuth](http://dev.bukkit.org/bukkit-plugins/ultraauth-aa/)
* [CrazyLogin](https://dev.bukkit.org/bukkit-plugins/crazylogin/)
* [LoginSecurity](https://dev.bukkit.org/bukkit-plugins/loginsecurity/)
* [RoyalAuth](https://dev.bukkit.org/bukkit-plugins/royalauth/)
* [UltraAuth](https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/)
####BungeeCord/Waterfall
#### BungeeCord/Waterfall
* [BungeeAuth](https://www.spigotmc.org/resources/bungeeauth.493/)
###Downloads
### Downloads
https://www.spigotmc.org/resources/fastlogin.14153/history
***
###How to install
### How to install
####Bukkit/Spigot/PaperSpigot
#### Bukkit/Spigot/Paper
1. Download and install ProtocolLib
2. Download and install FastLogin
3. Set your server in offline mode by setting the value onlinemode in your server.properties to false
####BungeeCord/Waterfall
#### BungeeCord/Waterfall
1. Activate BungeeCord in the Spigot configuration
2. Restart your server
@@ -86,13 +83,14 @@ Put your stats id from the BungeeCord config into this file
5. Download and Install FastLogin on BungeeCord AND Spigot
6. Check your database settings in the config of FastLogin on BungeeCord
7. Set your proxy (BungeeCord) in offline mode by setting the value onlinemode in your config.yml to false
8. (BungeeCord doesn't support SQLite per default, so you should change the configuration to MySQL or MariaDB)
8. You should *always* firewall your spigot server that it's only accessible through BungeeCord https://www.spigotmc.org/wiki/bungeecord-installation/#post-installation
9. (BungeeCord doesn't support SQLite per default, so you should change the configuration to MySQL or MariaDB)
***
###FAQ
### FAQ
####Index
#### Index
1. [How does Minecraft logins work?](#how-does-minecraft-logins-work)
2. [How does this plugin work?](#how-does-this-plugin-work)
3. [Why does the plugin require offline mode?](#why-does-the-plugin-require-offline-mode)
@@ -103,26 +101,26 @@ Put your stats id from the BungeeCord config into this file
8. [Could premium players have a premium UUID and Skin?](#could-premium-players-have-a-premium-uuid-and-skin)
9. [Is this plugin compatible with Cauldron?](#is-this-plugin-compatible-with-cauldron)
####How does minecraft logins work?
######Online Mode
#### How does minecraft logins work?
###### Online Mode
1. Client -> Server: I want to login, here is my username
2. Server -> Client: Okay. I'm in online mode so here is my public key for encryption and my serverid
3. Client -> Mojang: I'm player "xyz". I want to join a server with that serverid
2. Server -> Client: Okay. I'm in online mode so here is my public key for encryption and my server id
3. Client -> Mojang: I'm player "xyz". I want to join a server with that server id
4. Mojang -> Client: Session data checked. You can continue
5. Client -> Server: I received a successful response from Mojang. Heres our shared secret key
5. Client -> Server: I received a successful response from Mojang. Here our shared secret key
6. Server -> Mojang: Does the player "xyz" with this shared secret key has a valid account to join me?
7. Mojang -> Server: Yes, the player has the following additionally properties (UUID, Skin)
8. Client and Server: encrypt all following communication packet
9. Server -> Client: Everything checked you can play now
######Offline Mode
###### Offline Mode
In offline mode step 2-7 is skipped. So a login request is directly followed by 8.
######More details
###### More details
http://wiki.vg/Protocol#Login
####How does this plugin work?
#### How does this plugin work?
By using ProtocolLib, this plugin works as a proxy between the client and server. This plugin will fake that the server
runs in online mode. It does everything an online mode server would do. This will be for example, generating keys or
checking for valid sessions. Because everything is the same compared to an offline mode login after an encrypted
@@ -137,7 +135,7 @@ connection, we will intercept only **login** packets of **premium** players.
6. Encrypt the connection
7. On success intercept all related login packets and fake a new login packet as a normal offline login
####Why does the plugin require offline mode?
#### Why does the plugin require offline mode?
1. As you can see in the question "how does minecraft login works", offline mode is equivalent to online mode except of
the encryption and session checks on login. So we can intercept and cancel the first packets for premium players and
enable an encrypted connection. Then we send a new fake packet in order to pretend that this a new login request from
@@ -147,38 +145,38 @@ incorrectly. For example, a plugin tries to fetch the UUID from Mojang, but the
a paid account.
3. Servers, who allow cracked players and just speed up logins for premium players, are **already** in offline mode.
####Can cracked player join with premium usernames?
#### Can cracked player join with premium usernames?
Yes, indeed. Therefore the command for toggling the fast login method exists.
####Why do players have to invoke a command?
#### Why do players have to invoke a command?
1. It's a secure way to make sure a person with a paid account cannot steal the account
of a cracked player that has the same username. The player have to proof first that it's his own account.
2. We only receive the username from the player on login. We could check if that username is associated
to a paid account but if we request a online mode login from a cracked player (who uses a username from
a paid account), the player will disconnect with the reason "bad login" or "Invalid session". There is no way to change
that message on the server side (without client modifications), because it's a connection between the Client and the
Sessionserver.
session-server.
3. If a premium player would skip registration too, a player of a cracked account could later still register the
account and would claim and steal the account from the premium player. Because commands cannot be invoked unless the
player has a account or is logged in, protects this method also premium players
###What happens if a paid account joins with a used username?
### What happens if a paid account joins with a used username?
The player on the server have to activate the feature of this plugin by command. If a person buys the username
of his own account, it's still secured. A normal offline mode login makes sure he's the owner of the server account
and Mojang account. Then the command can be executed. So someone different cannot steal the account of cracked player
by buying the username.
####Does the plugin have BungeeCord support?
#### Does the plugin have BungeeCord support?
Yes it has. See the how to install above.
####Could premium players have a premium UUID and Skin?
#### Could premium players have a premium UUID and Skin?
Since 0.7 both features are implemented. You can check the config.yml in order to activate it.
####Is this plugin compatible with Cauldron?
#### Is this plugin compatible with Cauldron?
It's not tested yet, but all needed methods also exists in Cauldron so it could work together.
***
###Useful Links:
### Useful Links:
* [Login Protocol](http://wiki.vg/Protocol#Login)
* [Protocol Encryption](http://wiki.vg/Protocol_Encryption)
* [Protocol Encryption](http://wiki.vg/Protocol_Encryption)

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.9</version>
<version>1.10</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -34,10 +34,10 @@
<url>http://repo.dmulloy2.net/content/groups/public/</url>
</repository>
<!--Authme Reloaded-->
<!--AuthMe Reloaded-->
<repository>
<id>xephi-repo</id>
<url>http://ci.xephi.fr/plugin/repository/everything/</url>
<url>https://ci.xephi.fr/plugin/repository/everything/</url>
</repository>
<!--xAuth-->
@@ -51,6 +51,12 @@
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<!--PlaceholderAPI -->
<repository>
<id>placeholderapi</id>
<url>http://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository>
</repositories>
<dependencies>
@@ -65,7 +71,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.10-R0.1-SNAPSHOT</version>
<version>1.12-pre2-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
@@ -73,21 +79,35 @@
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>3.6.5</version>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>com.github.ProtocolSupport</groupId>
<artifactId>ProtocolSupport</artifactId>
<dependency>
<groupId>com.github.ProtocolSupport</groupId>
<artifactId>ProtocolSupport</artifactId>
<!--4.25.dev-->
<version>5554413b51</version>
</dependency>
<version>a4f060dc46</version>
</dependency>
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.8.1</version>
<scope>provided</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Login Plugins-->
<dependency>
<groupId>fr.xephi</groupId>
<artifactId>authme</artifactId>
<version>5.2-SNAPSHOT</version>
<version>5.3.1</version>
<optional>true</optional>
<exclusions>
<exclusion>
@@ -100,7 +120,7 @@
<dependency>
<groupId>com.lenis0012.bukkit</groupId>
<artifactId>loginsecurity</artifactId>
<version>2.1.3-SNAPSHOT</version>
<version>2.1.6</version>
<optional>true</optional>
<exclusions>
<exclusion>
@@ -110,10 +130,10 @@
</exclusions>
</dependency>
<dependency>
<groupId>com.github.games647</groupId>
<artifactId>LogIt</artifactId>
<version>9e3581db27</version>
<dependency>
<groupId>com.github.games647</groupId>
<artifactId>LogIt</artifactId>
<version>9e3581db27</version>
<optional>true</optional>
<scope>provided</scope>
<exclusions>
@@ -122,7 +142,7 @@
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependency>
<dependency>
<groupId>com.github.RoyalDev</groupId>

View File

@@ -1,72 +0,0 @@
package com.github.games647.fastlogin.bukkit;
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<Player> {
private final FastLoginBukkit plugin;
public BukkitCore(FastLoginBukkit plugin) {
super(plugin.getConfig().getValues(false));
this.plugin = plugin;
}
@Override
public File getDataFolder() {
return plugin.getDataFolder();
}
@Override
public Logger getLogger() {
return plugin.getLogger();
}
@Override
public ThreadFactory getThreadFactory() {
String pluginName = plugin.getName();
return new ThreadFactoryBuilder()
.setNameFormat(pluginName + " Database Pool Thread #%1$d")
//Hikari create daemons by default
.setDaemon(true)
.build();
}
@Override
public void loadMessages() {
plugin.saveResource("messages.yml", false);
File messageFile = new File(plugin.getDataFolder(), "messages.yml");
YamlConfiguration messageConfig = YamlConfiguration.loadConfiguration(messageFile);
InputStreamReader defaultReader = new InputStreamReader(plugin.getResource("messages.yml"), Charsets.UTF_8);
YamlConfiguration defaults = YamlConfiguration.loadConfiguration(defaultReader);
messageConfig.setDefaults(defaults);
messageConfig.getKeys(false).forEach((key) -> {
String message = ChatColor.translateAlternateColorCodes('&', messageConfig.getString(key));
if (!message.isEmpty()) {
localeMessages.put(key, message);
}
});
}
@Override
public MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests) {
return new MojangApiBukkit(logger, addresses, requests);
}
}

View File

@@ -28,7 +28,7 @@ public class BukkitLoginSession extends LoginSession {
this.verifyToken = ArrayUtils.clone(verifyToken);
}
//available for bungeecord
//available for BungeeCord
public BukkitLoginSession(String username, boolean registered) {
this(username, "", ArrayUtils.EMPTY_BYTE_ARRAY, registered, null);
}
@@ -41,7 +41,7 @@ public class BukkitLoginSession extends LoginSession {
/**
* Gets the random generated server id. This makes sure the request sent from the client is just for this server.
*
* See this for details http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/
* See this for details http://www.sk89q.com/2011/09/Minecraft-name-spoofing-exploit/
*
* Empty if it's a BungeeCord connection
*

View File

@@ -8,8 +8,6 @@ import java.security.KeyPair;
import java.security.KeyPairGenerator;
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;
@@ -41,14 +39,14 @@ public class EncryptionUtil {
}
}
public static byte[] getServerIdHash(String serverId, PublicKey publicKey, SecretKey secretKey) {
public static byte[] getServerIdHash(String serverId, Key publicKey, Key secretKey) {
return digestOperation("SHA-1"
, new byte[][]{serverId.getBytes(Charsets.ISO_8859_1), secretKey.getEncoded(), publicKey.getEncoded()});
, serverId.getBytes(Charsets.ISO_8859_1), secretKey.getEncoded(), publicKey.getEncoded());
}
private static byte[] digestOperation(String algo, byte[]... content) {
private static byte[] digestOperation(String algorithm, byte[]... content) {
try {
MessageDigest messagedigest = MessageDigest.getInstance(algo);
MessageDigest messagedigest = MessageDigest.getInstance(algorithm);
Stream.of(content).forEach(messagedigest::update);
return messagedigest.digest();
@@ -72,7 +70,7 @@ public class EncryptionUtil {
// return null;
// }
public static SecretKey decryptSharedKey(PrivateKey privateKey, byte[] encryptedSharedKey) {
public static SecretKey decryptSharedKey(Key privateKey, byte[] encryptedSharedKey) {
return new SecretKeySpec(decryptData(privateKey, encryptedSharedKey), "AES");
}
@@ -83,8 +81,8 @@ public class EncryptionUtil {
private static byte[] cipherOperation(int operationMode, Key key, byte[] data) {
try {
return createCipherInstance(operationMode, key.getAlgorithm(), key).doFinal(data);
} catch (IllegalBlockSizeException | BadPaddingException illegalblocksizeexception) {
illegalblocksizeexception.printStackTrace();
} catch (IllegalBlockSizeException | BadPaddingException ex) {
ex.printStackTrace();
}
System.err.println("Cipher data failed!");
@@ -97,8 +95,8 @@ public class EncryptionUtil {
cipher.init(operationMode, key);
return cipher;
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException invalidkeyexception) {
invalidkeyexception.printStackTrace();
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException ex) {
ex.printStackTrace();
}
System.err.println("Cipher creation failed!");

View File

@@ -1,66 +1,65 @@
package com.github.games647.fastlogin.bukkit;
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;
import com.github.games647.fastlogin.bukkit.listener.BungeeCordListener;
import com.github.games647.fastlogin.bukkit.listener.protocollib.EncryptionPacketListener;
import com.github.games647.fastlogin.bukkit.listener.protocollib.LoginSkinApplyListener;
import com.github.games647.fastlogin.bukkit.listener.protocollib.StartPacketListener;
import com.github.games647.fastlogin.bukkit.listener.protocollib.ProtocolLibListener;
import com.github.games647.fastlogin.bukkit.listener.protocolsupport.ProtocolSupportListener;
import com.github.games647.fastlogin.bukkit.tasks.DelayedAuthHook;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.MojangApiConnector;
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
import com.google.common.collect.Iterables;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import java.io.Reader;
import java.security.KeyPair;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.messaging.PluginMessageRecipient;
/**
* This plugin checks if a player has a paid account and if so tries to skip offline mode authentication.
*/
public class FastLoginBukkit extends JavaPlugin {
private static final int WORKER_THREADS = 3;
public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<CommandSender> {
//provide a immutable key pair to be thread safe | used for encrypting and decrypting traffic
private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
private boolean bungeeCord;
private BukkitCore core;
private FastLoginCore<Player, CommandSender, FastLoginBukkit> core;
private boolean serverStarted;
//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 final ConcurrentMap<String, BukkitLoginSession> loginSession = FastLoginCore.buildCache(1, -1);
@Override
public void onEnable() {
saveDefaultConfig();
core = new BukkitCore(this);
core.loadMessages();
core.setApiConnector();
core = new FastLoginCore<>(this);
core.load();
try {
if (ClassUtil.isPresent("org.spigotmc.SpigotConfig")) {
bungeeCord = Class.forName("org.spigotmc.SpigotConfig").getDeclaredField("bungee").getBoolean(null);
}
bungeeCord = Class.forName("org.spigotmc.SpigotConfig").getDeclaredField("bungee").getBoolean(null);
} catch (ClassNotFoundException notFoundEx) {
//ignore server has no bungee support
} catch (Exception ex) {
getLogger().log(Level.WARNING, "Cannot check bungeecord support. You use a non-spigot build", ex);
}
if (getServer().getOnlineMode()) {
//we need to require offline to prevent a session request for a offline player
//we need to require offline to prevent a loginSession request for a offline player
getLogger().severe("Server have to be in offline mode");
setEnabled(false);
return;
@@ -82,14 +81,11 @@ public class FastLoginBukkit extends JavaPlugin {
if (getServer().getPluginManager().isPluginEnabled("ProtocolSupport")) {
getServer().getPluginManager().registerEvents(new ProtocolSupportListener(this), this);
} else if (getServer().getPluginManager().isPluginEnabled("ProtocolLib")) {
//we are performing HTTP request on these so run it async (seperate from the Netty IO threads)
AsynchronousManager asynchronousManager = ProtocolLibrary.getProtocolManager().getAsynchronousManager();
//they will be created with a static builder, because otherwise it will throw a
//java.lang.NoClassDefFoundError: com/comphenix/protocol/events/PacketListener if ProtocolSupport was
//only found
ProtocolLibListener.register(this);
StartPacketListener startPacketListener = new StartPacketListener(this);
EncryptionPacketListener encryptionPacketListener = new EncryptionPacketListener(this);
asynchronousManager.registerAsyncHandler(startPacketListener).start(WORKER_THREADS);
asynchronousManager.registerAsyncHandler(encryptionPacketListener).start(WORKER_THREADS);
getServer().getPluginManager().registerEvents(new LoginSkinApplyListener(this), this);
} else {
getLogger().warning("Either ProtocolLib or ProtocolSupport have to be installed "
@@ -98,19 +94,23 @@ public class FastLoginBukkit extends JavaPlugin {
}
//delay dependency setup because we load the plugin very early where plugins are initialized yet
getServer().getScheduler().runTask(this, new DelayedAuthHook(this));
getServer().getScheduler().runTaskLater(this, new DelayedAuthHook(this), 5L);
getServer().getPluginManager().registerEvents(new BukkitJoinListener(this), this);
//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));
if (getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) {
//prevents NoClassDef errors if it's not available
PremiumPlaceholder.register(this);
}
}
@Override
public void onDisable() {
session.clear();
loginSession.clear();
if (core != null) {
core.close();
@@ -120,13 +120,13 @@ public class FastLoginBukkit extends JavaPlugin {
getServer().getOnlinePlayers().forEach(player -> player.removeMetadata(getName(), this));
}
public BukkitCore getCore() {
public FastLoginCore<Player, CommandSender, FastLoginBukkit> getCore() {
return core;
}
public void sendBungeeActivateMessage(CommandSender sender, String target, boolean activate) {
if (sender instanceof Player) {
notifiyBungeeCord((Player) sender, target, activate);
notifyBungeeCord((Player) sender, target, activate, sender instanceof Player);
} else {
Player firstPlayer = Iterables.getFirst(getServer().getOnlinePlayers(), null);
if (firstPlayer == null) {
@@ -134,23 +134,18 @@ public class FastLoginBukkit extends JavaPlugin {
return;
}
notifiyBungeeCord(firstPlayer, target, activate);
notifyBungeeCord(firstPlayer, target, activate, sender instanceof Player);
}
}
@Deprecated
public void setPasswordGenerator(PasswordGenerator passwordGenerator) {
core.setPasswordGenerator(passwordGenerator);
}
/**
* Gets a thread-safe map about players which are connecting to the server are being checked to be premium (paid
* account)
*
* @return a thread-safe session map
* @return a thread-safe loginSession map
*/
public ConcurrentMap<String, BukkitLoginSession> getSessions() {
return session;
public ConcurrentMap<String, BukkitLoginSession> getLoginSessions() {
return loginSession;
}
/**
@@ -162,22 +157,6 @@ public class FastLoginBukkit extends JavaPlugin {
return keyPair;
}
/**
* Gets the auth plugin hook in order to interact with the plugins. This can be null if no supporting auth plugin
* was found.
*
* @return interface to any supported auth plugin
*/
@Deprecated
public BukkitAuthPlugin getAuthPlugin() {
return (BukkitAuthPlugin) core.getAuthPluginHook();
}
@Deprecated
public void setAuthPluginHook(BukkitAuthPlugin authPlugin) {
core.setAuthPluginHook(authPlugin);
}
public boolean isBungeeCord() {
return bungeeCord;
}
@@ -198,7 +177,7 @@ public class FastLoginBukkit extends JavaPlugin {
}
}
private void notifiyBungeeCord(Player sender, String target, boolean activate) {
private void notifyBungeeCord(PluginMessageRecipient sender, String target, boolean activate, boolean isPlayer) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
if (activate) {
dataOutput.writeUTF("ON");
@@ -207,6 +186,35 @@ public class FastLoginBukkit extends JavaPlugin {
}
dataOutput.writeUTF(target);
dataOutput.writeBoolean(isPlayer);
sender.sendPluginMessage(this, getName(), dataOutput.toByteArray());
}
@Override
public Map<String, Object> loadYamlFile(Reader reader) {
YamlConfiguration config = YamlConfiguration.loadConfiguration(reader);
return config.getValues(true);
}
@Override
public void sendMessage(CommandSender receiver, String message) {
receiver.sendMessage(message);
}
@Override
public String translateColorCodes(char colorChar, String rawMessage) {
return ChatColor.translateAlternateColorCodes(colorChar, rawMessage);
}
@Override
public ThreadFactory getThreadFactory() {
//not required here to make a custom thread factory
return null;
}
@Override
public MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests
, Map<String, Integer> proxies) {
return new MojangApiBukkit(logger, addresses, requests, proxies);
}
}

View File

@@ -7,7 +7,10 @@ import com.github.games647.fastlogin.core.shared.MojangApiConnector;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -18,22 +21,27 @@ import org.json.simple.JSONValue;
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?";
private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?" +
"username=%s&serverId=%s";
public MojangApiBukkit(Logger logger, List<String> localAddresses, int rateLimit) {
super(logger, localAddresses, rateLimit);
public MojangApiBukkit(Logger logger, List<String> localAddresses, int rateLimit, Map<String, Integer> proxies) {
super(logger, localAddresses, rateLimit, proxies);
}
@Override
public boolean hasJoinedServer(LoginSession session, String serverId) {
public boolean hasJoinedServer(LoginSession session, String serverId, InetSocketAddress ip) {
BukkitLoginSession playerSession = (BukkitLoginSession) session;
try {
String url = HAS_JOINED_URL + "username=" + playerSession.getUsername() + "&serverId=" + serverId;
String url = String.format(HAS_JOINED_URL, playerSession.getUsername(), serverId);
if (ip != null) {
url += "&ip=" + URLEncoder.encode(ip.getAddress().getHostAddress(), "UTF-8");
}
HttpURLConnection conn = getConnection(url);
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = reader.readLine();
if (line != null && !line.equals("null")) {
if (line != null && !"null".equals(line)) {
//validate parsing
//http://wiki.vg/Protocol_Encryption#Server
JSONObject userData = (JSONObject) JSONValue.parseWithException(line);
@@ -44,7 +52,7 @@ public class MojangApiBukkit extends MojangApiConnector {
JSONObject skinProperty = (JSONObject) properties.get(0);
String propertyName = (String) skinProperty.get("name");
if (propertyName.equals("textures")) {
if ("textures".equals(propertyName)) {
String skinValue = (String) skinProperty.get("value");
String signature = (String) skinProperty.get("signature");
playerSession.setSkin(skinValue, signature);
@@ -53,7 +61,7 @@ public class MojangApiBukkit extends MojangApiConnector {
return true;
}
} catch (Exception ex) {
//catch not only ioexceptions also parse and NPE on unexpected json format
//catch not only io-exceptions also parse and NPE on unexpected json format
logger.log(Level.WARNING, "Failed to verify session", ex);
}

View File

@@ -1,14 +0,0 @@
package com.github.games647.fastlogin.bukkit;
import org.bukkit.entity.Player;
/**
*
* @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

@@ -0,0 +1,40 @@
package com.github.games647.fastlogin.bukkit;
import java.util.List;
import me.clip.placeholderapi.PlaceholderAPI;
import me.clip.placeholderapi.PlaceholderHook;
import org.bukkit.entity.Player;
import org.bukkit.metadata.MetadataValue;
public class PremiumPlaceholder extends PlaceholderHook {
private final FastLoginBukkit plugin;
public PremiumPlaceholder(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@Override
public String onPlaceholderRequest(Player player, String variable) {
if (player != null && "fastlogin_status".contains(variable)) {
List<MetadataValue> metadata = player.getMetadata(plugin.getName());
if (metadata == null) {
return "unknown";
}
if (!metadata.isEmpty()) {
return "premium";
} else {
return "cracked";
}
}
return null;
}
public static boolean register(FastLoginBukkit plugin) {
return PlaceholderAPI.registerPlaceholderHook(plugin, new PremiumPlaceholder(plugin));
}
}

View File

@@ -28,15 +28,12 @@ public class CrackedCommand implements CommandExecutor {
if (plugin.isBungeeCord()) {
plugin.sendBungeeActivateMessage(sender, sender.getName(), false);
String message = plugin.getCore().getMessage("wait-on-proxy");
if (message != null) {
sender.sendMessage(message);
}
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
} else {
//todo: load async if
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
if (profile.isPremium()) {
sender.sendMessage(plugin.getCore().getMessage("remove-premium"));
plugin.getCore().sendLocaleMessage("remove-premium", sender);
profile.setPremium(false);
profile.setUuid(null);
@@ -44,7 +41,7 @@ public class CrackedCommand implements CommandExecutor {
plugin.getCore().getStorage().save(profile);
});
} else {
sender.sendMessage(plugin.getCore().getMessage("not-premium"));
plugin.getCore().sendLocaleMessage("not-premium", sender);
}
}
@@ -58,29 +55,27 @@ public class CrackedCommand implements CommandExecutor {
private void onCrackedOther(CommandSender sender, Command command, String[] args) {
if (!sender.hasPermission(command.getPermission() + ".other")) {
sender.sendMessage(plugin.getCore().getMessage("no-permission"));
plugin.getCore().sendLocaleMessage("no-permission", sender);
return;
}
if (plugin.isBungeeCord()) {
plugin.sendBungeeActivateMessage(sender, args[0], false);
String message = plugin.getCore().getMessage("wait-on-proxy");
if (message != null) {
sender.sendMessage(message);
}
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
} else {
//todo: load async
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(args[0]);
if (profile == null) {
sender.sendMessage("Error occured");
sender.sendMessage("Error occurred");
return;
}
//existing player is already cracked
if (profile.getUserId() != -1 && !profile.isPremium()) {
sender.sendMessage(plugin.getCore().getMessage("not-premium-other"));
plugin.getCore().sendLocaleMessage("not-premium-other", sender);
} else {
sender.sendMessage(plugin.getCore().getMessage("remove-premium"));
plugin.getCore().sendLocaleMessage("remove-premium", sender);
profile.setPremium(false);
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
plugin.getCore().getStorage().save(profile);

View File

@@ -1,83 +0,0 @@
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

@@ -29,16 +29,13 @@ public class PremiumCommand implements CommandExecutor {
if (args.length == 0) {
if (!(sender instanceof Player)) {
//console or command block
sender.sendMessage(plugin.getCore().getMessage("no-console"));
plugin.getCore().sendLocaleMessage("no-console", sender);
return true;
}
if (plugin.isBungeeCord()) {
plugin.sendBungeeActivateMessage(sender, sender.getName(), true);
String message = plugin.getCore().getMessage("wait-on-proxy");
if (message != null) {
sender.sendMessage(message);
}
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
} else {
UUID id = ((Player) sender).getUniqueId();
if (plugin.getConfig().getBoolean("premium-warning")
@@ -52,7 +49,7 @@ public class PremiumCommand implements CommandExecutor {
//todo: load async
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
if (profile.isPremium()) {
sender.sendMessage(plugin.getCore().getMessage("already-exists"));
plugin.getCore().sendLocaleMessage("already-exists", sender);
} else {
//todo: resolve uuid
profile.setPremium(true);
@@ -60,7 +57,7 @@ public class PremiumCommand implements CommandExecutor {
plugin.getCore().getStorage().save(profile);
});
sender.sendMessage(plugin.getCore().getMessage("add-premium"));
plugin.getCore().sendLocaleMessage("add-premium", sender);
}
}
@@ -74,26 +71,23 @@ public class PremiumCommand implements CommandExecutor {
private void onPremiumOther(CommandSender sender, Command command, String[] args) {
if (!sender.hasPermission(command.getPermission() + ".other")) {
sender.sendMessage(plugin.getCore().getMessage("no-permission"));
plugin.getCore().sendLocaleMessage("no-permission", sender);
return ;
}
if (plugin.isBungeeCord()) {
plugin.sendBungeeActivateMessage(sender, args[0], true);
String message = plugin.getCore().getMessage("wait-on-proxy");
if (message != null) {
sender.sendMessage(message);
}
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
} else {
//todo: load async
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(args[0]);
if (profile == null) {
sender.sendMessage(plugin.getCore().getMessage("player-unknown"));
plugin.getCore().sendLocaleMessage("player-unknown", sender);
return;
}
if (profile.isPremium()) {
sender.sendMessage(plugin.getCore().getMessage("already-exists-other"));
plugin.getCore().sendLocaleMessage("already-exists-other", sender);
} else {
//todo: resolve uuid
profile.setPremium(true);
@@ -101,7 +95,7 @@ public class PremiumCommand implements CommandExecutor {
plugin.getCore().getStorage().save(profile);
});
sender.sendMessage(plugin.getCore().getMessage("add-premium-other"));
plugin.getCore().sendLocaleMessage("add-premium-other", sender);
}
}
}

View File

@@ -1,59 +1,72 @@
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;
import fr.xephi.authme.api.v3.AuthMeApi;
import org.bukkit.entity.Player;
/**
* Github: https://github.com/Xephi/AuthMeReloaded/
*
* Project page:
*
* Bukkit: http://dev.bukkit.org/bukkit-plugins/authme-reloaded/
*
* Spigot: https://www.spigotmc.org/resources/authme-reloaded.6269/
*/
public class AuthMeHook implements AuthPlugin<Player> {
private final boolean isNewAPIAvailable;
private final boolean v3APIAvailable;
public AuthMeHook() {
this.isNewAPIAvailable = ClassUtil.isPresent("fr.xephi.authme.api.NewAPI");
boolean apiAvailable = true;
try {
Class.forName("fr.xephi.authme.api.v3.AuthMeApi");
} catch (ClassNotFoundException classNotFoundEx) {
apiAvailable = false;
}
this.v3APIAvailable = apiAvailable;
}
@Override
@SuppressWarnings("deprecation")
public boolean forceLogin(Player player) {
//skips registration and login
if (isNewAPIAvailable) {
NewAPI.getInstance().forceLogin(player);
if (v3APIAvailable) {
//skips registration and login
if (AuthMeApi.getInstance().isAuthenticated(player)) {
return false;
} else {
AuthMeApi.getInstance().forceLogin(player);
}
} else {
API.forceLogin(player);
//skips registration and login
if (NewAPI.getInstance().isAuthenticated(player)) {
return false;
} else {
NewAPI.getInstance().forceLogin(player);
}
}
return true;
}
@Override
@SuppressWarnings("deprecation")
public boolean isRegistered(String playerName) throws Exception {
if (isNewAPIAvailable) {
return NewAPI.getInstance().isRegistered(playerName);
} else {
return API.isRegistered(playerName);
if (v3APIAvailable) {
return AuthMeApi.getInstance().isRegistered(playerName);
}
return NewAPI.getInstance().isRegistered(playerName);
}
@Override
@SuppressWarnings("deprecation")
public boolean forceRegister(Player player, String password) {
if (isNewAPIAvailable) {
NewAPI.getInstance().forceRegister(player, password);
//this automatically registers the player too
if (v3APIAvailable) {
AuthMeApi.getInstance().forceRegister(player, password);
} else {
API.registerPlayer(player.getName(), password);
forceLogin(player);
NewAPI.getInstance().forceRegister(player, password);
}
return true;

View File

@@ -1,21 +0,0 @@
package com.github.games647.fastlogin.bukkit.hooks;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import org.bukkit.entity.Player;
/**
* @deprecated please use com.github.games647.fastlogin.core.hooks.AuthPlugin<org.bukkit.entity.Player>
*/
@Deprecated
public interface BukkitAuthPlugin extends AuthPlugin<Player> {
@Override
boolean forceLogin(Player player);
@Override
boolean isRegistered(String playerName) throws Exception;
@Override
boolean forceRegister(Player player, String password);
}

View File

@@ -38,7 +38,7 @@ public class CrazyLoginHook implements AuthPlugin<Player> {
playerData.setLoggedIn(true);
String ip = player.getAddress().getAddress().getHostAddress();
//this should be done after login to restore the inventory, unhide players, prevent potential memory leaks...
//this should be done after login to restore the inventory, show 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);

View File

@@ -5,7 +5,7 @@ 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;
import io.github.lucaseasedup.logit.session.SessionManager;
import org.bukkit.entity.Player;
/**
@@ -19,7 +19,12 @@ public class LogItHook implements AuthPlugin<Player> {
@Override
public boolean forceLogin(Player player) {
return LogItCore.getInstance().getSessionManager().startSession(player) == CancelledState.NOT_CANCELLED;
SessionManager sessionManager = LogItCore.getInstance().getSessionManager();
if (sessionManager.isSessionAlive(player)) {
return true;
}
return sessionManager.startSession(player) == CancelledState.NOT_CANCELLED;
}
@Override

View File

@@ -24,6 +24,10 @@ public class LoginSecurityHook implements AuthPlugin<Player> {
@Override
public boolean forceLogin(Player player) {
PlayerSession session = LoginSecurity.getSessionManager().getPlayerSession(player);
if (session.isAuthorized()) {
return true;
}
return session.performAction(new LoginAction(AuthService.PLUGIN, plugin)).isSuccess();
}

View File

@@ -8,6 +8,7 @@ import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.royaldev.royalauth.AuthPlayer;
import org.royaldev.royalauth.Config;
import org.royaldev.royalauth.RoyalAuth;
@@ -21,13 +22,17 @@ import org.royaldev.royalauth.RoyalAuth;
*/
public class RoyalAuthHook implements AuthPlugin<Player> {
private final RoyalAuth royalAuthPlugin = (RoyalAuth) Bukkit.getPluginManager().getPlugin("RoyalAuth");
private final Plugin royalAuthPlugin = (RoyalAuth) Bukkit.getPluginManager().getPlugin("RoyalAuth");
@Override
public boolean forceLogin(Player player) {
AuthPlayer authPlayer = AuthPlayer.getAuthPlayer(player);
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(royalAuthPlugin, () -> {
if (authPlayer.isLoggedIn()) {
return true;
}
//https://github.com/RoyalDev/RoyalAuth/blob/master/src/main/java/org/royaldev/royalauth/commands/CmdLogin.java#L62
//not thread-safe
authPlayer.login();

View File

@@ -9,7 +9,6 @@ import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import ultraauth.api.UltraAuthAPI;
import ultraauth.main.Main;
import ultraauth.managers.PlayerManager;
@@ -18,6 +17,7 @@ import ultraauth.managers.PlayerManager;
* Project page:
*
* Bukkit: http://dev.bukkit.org/bukkit-plugins/ultraauth-aa/
*
* Spigot: https://www.spigotmc.org/resources/ultraauth.17044/
*/
public class UltraAuthHook implements AuthPlugin<Player> {
@@ -28,6 +28,10 @@ public class UltraAuthHook implements AuthPlugin<Player> {
public boolean forceLogin(Player player) {
//not thread-safe
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(ultraAuthPlugin, () -> {
if (UltraAuthAPI.isAuthenticated(player)) {
return true;
}
UltraAuthAPI.authenticatedPlayer(player);
return UltraAuthAPI.isAuthenticated(player);
});
@@ -49,7 +53,7 @@ public class UltraAuthHook implements AuthPlugin<Player> {
public boolean forceRegister(Player player, String password) {
UltraAuthAPI.setPlayerPasswordOnline(player, password);
if (PlayerManager.getInstance().checkPlayerPassword(player, password)) {
//the register method silents any excpetion so check if our entry was saved
//the register method silents any exception so check if our entry was saved
return forceLogin(player);
}

View File

@@ -10,7 +10,6 @@ import java.util.concurrent.Future;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
@@ -30,6 +29,10 @@ public class xAuthHook implements AuthPlugin<Player> {
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(xAuthPlugin, () -> {
xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(player);
if (xAuthPlayer != null) {
if (xAuthPlayer.isAuthenticated()) {
return true;
}
//we checked that the player is premium (paid account)
xAuthPlayer.setPremium(true);
@@ -63,10 +66,9 @@ public class xAuthHook implements AuthPlugin<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 xAuthPlugin.getAuthClass(xAuthPlayer)
.adminRegister(player.getName(), password, null);
}
return false;

View File

@@ -40,7 +40,8 @@ public class BukkitJoinListener implements Listener {
if (!plugin.isBungeeCord()) {
//Wait before auth plugin and we received a message from BungeeCord initializes the player
Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, new ForceLoginTask(plugin, player), DELAY_LOGIN);
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player);
Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, forceLoginTask, DELAY_LOGIN);
}
}

View File

@@ -7,11 +7,10 @@ import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.util.List;
import java.nio.file.Path;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
@@ -19,7 +18,6 @@ import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.messaging.PluginMessageListener;
/**
@@ -48,19 +46,16 @@ public class BungeeCordListener implements PluginMessageListener {
}
ByteArrayDataInput dataInput = ByteStreams.newDataInput(message);
String subchannel = dataInput.readUTF();
plugin.getLogger().log(Level.FINEST, "Received plugin message for subchannel {0} from {1}"
, new Object[]{subchannel, player});
String subChannel = dataInput.readUTF();
plugin.getLogger().log(Level.FINEST, "Received plugin message for sub channel {0} from {1}"
, new Object[]{subChannel, player});
String playerName = dataInput.readUTF();
//check if the player is still online or disconnected
Player checkedPlayer = plugin.getServer().getPlayerExact(playerName);
//fail if target player is blacklisted because already authed or wrong bungeecord id
//fail if target player is blacklisted because already authenticated or wrong bungeecord id
if (checkedPlayer != null && !checkedPlayer.hasMetadata(plugin.getName())) {
//blacklist this target player for BungeeCord Id brute force attacks
player.setMetadata(plugin.getName(), new FixedMetadataValue(plugin, true));
//bungeecord UUID
long mostSignificantBits = dataInput.readLong();
long leastSignificantBits = dataInput.readLong();
@@ -69,7 +64,7 @@ public class BungeeCordListener implements PluginMessageListener {
//fail if BungeeCord support is disabled (id = null)
if (proxyIds.contains(sourceId)) {
readMessage(checkedPlayer, subchannel, playerName, player);
readMessage(checkedPlayer, subChannel, playerName, player);
}
}
}
@@ -80,8 +75,8 @@ public class BungeeCordListener implements PluginMessageListener {
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));
plugin.getLoginSessions().put(id, playerSession);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new ForceLoginTask(plugin.getCore(), player));
} else if ("AUTO_REGISTER".equalsIgnoreCase(subchannel)) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook();
@@ -90,8 +85,8 @@ public class BungeeCordListener implements PluginMessageListener {
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();
plugin.getLoginSessions().put(id, playerSession);
new ForceLoginTask(plugin.getCore(), player).run();
}
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to query isRegistered", ex);
@@ -101,14 +96,16 @@ public class BungeeCordListener implements PluginMessageListener {
}
public Set<UUID> loadBungeeCordIds() {
File whitelistFile = new File(plugin.getDataFolder(), FILE_NAME);
Path whitelistFile = plugin.getDataFolder().toPath().resolve(FILE_NAME);
try {
if (!whitelistFile.exists()) {
whitelistFile.createNewFile();
if (Files.notExists(whitelistFile)) {
Files.createFile(whitelistFile);
}
List<String> lines = Files.readAllLines(whitelistFile.toPath());
return lines.stream().map(String::trim).map(UUID::fromString).collect(Collectors.toSet());
return Files.lines(whitelistFile)
.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,65 +0,0 @@
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
* Handles incoming encryption responses from connecting clients.
* It prevents them from reaching the server because that cannot handle
* it in offline mode.
*
* Moreover this manages a started premium check from
* this plugin. So check if all data is correct and we can prove him as a
* owner of a paid minecraft account.
*
* Receiving packet information:
* http://wiki.vg/Protocol#Encryption_Response
*
* sharedSecret=encrypted byte array
* verify token=encrypted byte array
*/
public class EncryptionPacketListener extends PacketAdapter {
//hides the inherit Plugin plugin field, but we need this type
private final FastLoginBukkit plugin;
public EncryptionPacketListener(FastLoginBukkit plugin) {
//run async in order to not block the server, because we make api calls to Mojang
super(params(plugin, PacketType.Login.Client.ENCRYPTION_BEGIN).optionAsync());
this.plugin = plugin;
}
/**
* C->S : Handshake State=2
* C->S : Login Start
* S->C : Encryption Key Request
* (Client Auth)
* C->S : Encryption Key Response
* (Server Auth, Both enable encryption)
* S->C : Login Success (*)
*
* On offline logins is Login Start followed by Login Success
*
* Minecraft Server implementation
* https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L180
*/
@Override
public void onPacketReceiving(PacketEvent packetEvent) {
if (packetEvent.isCancelled()) {
return;
}
Player sender = packetEvent.getPlayer();
byte[] sharedSecret = packetEvent.getPacket().getByteArrays().read(0);
packetEvent.getAsyncMarker().incrementProcessingDelay();
VerifyResponseTask verifyTask = new VerifyResponseTask(plugin, packetEvent, sender, sharedSecret);
Bukkit.getScheduler().runTaskAsynchronously(plugin, verifyTask);
}
}

View File

@@ -42,8 +42,8 @@ public class LoginSkinApplyListener implements Listener {
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
for (BukkitLoginSession session : plugin.getSessions().values()) {
//loginEvent.getAddress is just a InetAddress not InetSocketAddress, so not unique enough
for (BukkitLoginSession session : plugin.getLoginSessions().values()) {
if (session.getUsername().equals(player.getName())) {
String signature = session.getSkinSignature();
String skinData = session.getEncodedSkinData();

View File

@@ -10,9 +10,11 @@ import com.github.games647.fastlogin.core.shared.JoinManagement;
import java.util.Random;
import java.util.logging.Level;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class NameCheckTask extends JoinManagement<Player, ProtocolLibLoginSource> implements Runnable {
public class NameCheckTask extends JoinManagement<Player, CommandSender, ProtocolLibLoginSource>
implements Runnable {
private final FastLoginBukkit plugin;
private final PacketEvent packetEvent;
@@ -53,13 +55,13 @@ public class NameCheckTask extends JoinManagement<Player, ProtocolLibLoginSource
}
String ip = player.getAddress().getAddress().getHostAddress();
core.getPendingLogins().put(ip + username, new Object());
core.getPendingLogin().put(ip + username, new Object());
String serverId = source.getServerId();
byte[] verify = source.getVerifyToken();
BukkitLoginSession playerSession = new BukkitLoginSession(username, serverId, verify, registered, profile);
plugin.getSessions().put(player.getAddress().toString(), playerSession);
plugin.getLoginSessions().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);
@@ -69,6 +71,6 @@ public class NameCheckTask extends JoinManagement<Player, ProtocolLibLoginSource
@Override
public void startCrackedSession(ProtocolLibLoginSource source, PlayerProfile profile, String username) {
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.getSessions().put(player.getAddress().toString(), loginSession);
plugin.getLoginSessions().put(player.getAddress().toString(), loginSession);
}
}

View File

@@ -0,0 +1,83 @@
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Login.Client;
import com.comphenix.protocol.ProtocolLibrary;
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 java.util.Random;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class ProtocolLibListener extends PacketAdapter {
private static final int WORKER_THREADS = 3;
private final FastLoginBukkit plugin;
//just create a new once on plugin enable. This used for verify token generation
private final Random random = new Random();
public ProtocolLibListener(FastLoginBukkit plugin) {
//run async in order to not block the server, because we are making api calls to Mojang
super(params().plugin(plugin)
.types(PacketType.Login.Client.START, PacketType.Login.Client.ENCRYPTION_BEGIN)
.optionAsync());
this.plugin = plugin;
}
public static void register(FastLoginBukkit plugin) {
ProtocolLibrary.getProtocolManager()
.getAsynchronousManager()
.registerAsyncHandler(new ProtocolLibListener(plugin))
.start(WORKER_THREADS);
}
@Override
public void onPacketReceiving(PacketEvent packetEvent) {
if (packetEvent.isCancelled()
|| plugin.getCore().getAuthPluginHook()== null
|| !plugin.isServerFullyStarted()) {
return;
}
Player sender = packetEvent.getPlayer();
PacketType packetType = packetEvent.getPacketType();
if (packetType == Client.START) {
onLogin(packetEvent, sender);
} else {
onEncryptionBegin(packetEvent, sender);
}
}
private void onEncryptionBegin(PacketEvent packetEvent, Player sender) {
byte[] sharedSecret = packetEvent.getPacket().getByteArrays().read(0);
packetEvent.getAsyncMarker().incrementProcessingDelay();
Runnable verifyTask = new VerifyResponseTask(plugin, packetEvent, sender, sharedSecret);
Bukkit.getScheduler().runTaskAsynchronously(plugin, verifyTask);
}
private void onLogin(PacketEvent packetEvent, Player player) {
//this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
String sessionKey = player.getAddress().toString();
//remove old data every time on a new login in order to keep the session only for one person
plugin.getLoginSessions().remove(sessionKey);
//player.getName() won't work at this state
PacketContainer packet = packetEvent.getPacket();
String username = packet.getGameProfiles().read(0).getName();
plugin.getLogger().log(Level.FINER, "Player {0} with {1} connecting", new Object[]{sessionKey, username});
packetEvent.getAsyncMarker().incrementProcessingDelay();
Runnable nameCheckTask = new NameCheckTask(plugin, packetEvent, random, player, username);
Bukkit.getScheduler().runTaskAsynchronously(plugin, nameCheckTask);
}
}

View File

@@ -1,77 +0,0 @@
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.PacketType;
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 java.util.Random;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
* Handles incoming start packets from connecting clients. It
* checks if we can start checking if the player is premium and
* start a request to the client that it should start online mode
* login.
*
* Receiving packet information:
* http://wiki.vg/Protocol#Login_Start
*
* String=Username
*/
public class StartPacketListener extends PacketAdapter {
//hides the inherit Plugin plugin field, but we need a more detailed type than just Plugin
private final FastLoginBukkit plugin;
//just create a new once on plugin enable. This used for verify token generation
private final Random random = new Random();
public StartPacketListener(FastLoginBukkit plugin) {
//run async in order to not block the server, because we are making api calls to Mojang
super(params(plugin, PacketType.Login.Client.START).optionAsync());
this.plugin = plugin;
}
/**
* C->S : Handshake State=2
* C->S : Login Start
* S->C : Encryption Key Request
* (Client Auth)
* C->S : Encryption Key Response
* (Server Auth, Both enable encryption)
* S->C : Login Success (*)
*
* On offline logins is Login Start followed by Login Success
*/
@Override
public void onPacketReceiving(PacketEvent packetEvent) {
if (packetEvent.isCancelled()
|| plugin.getCore().getAuthPluginHook()== null || !plugin.isServerFullyStarted()) {
return;
}
Player player = packetEvent.getPlayer();
//this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
String sessionKey = player.getAddress().toString();
//remove old data every time on a new login in order to keep the session only for one person
plugin.getSessions().remove(sessionKey);
//player.getName() won't work at this state
PacketContainer packet = packetEvent.getPacket();
String username = packet.getGameProfiles().read(0).getName();
plugin.getLogger().log(Level.FINER, "Player {0} with {1} connecting", new Object[]{sessionKey, username});
packetEvent.getAsyncMarker().incrementProcessingDelay();
NameCheckTask nameCheckTask = new NameCheckTask(plugin, packetEvent, random, player, username);
Bukkit.getScheduler().runTaskAsynchronously(plugin, nameCheckTask);
}
}

View File

@@ -5,7 +5,6 @@ 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;
@@ -18,6 +17,7 @@ import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.security.Key;
import java.security.PrivateKey;
import java.util.Arrays;
import java.util.UUID;
@@ -46,14 +46,11 @@ public class VerifyResponseTask implements Runnable {
@Override
public void run() {
try {
BukkitLoginSession session = plugin.getSessions().get(fromPlayer.getAddress().toString());
BukkitLoginSession session = plugin.getLoginSessions().get(fromPlayer.getAddress().toString());
if (session == null) {
disconnect(plugin.getCore().getMessage("invalid-requst"), true
disconnect(plugin.getCore().getMessage("invalid-request"), 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 {
@@ -84,8 +81,8 @@ public class VerifyResponseTask implements Runnable {
String serverId = (new BigInteger(serverIdHash)).toString(16);
String username = session.getUsername();
if (plugin.getCore().getApiConnector().hasJoinedServer(session, serverId)) {
plugin.getLogger().log(Level.FINE, "Player {0} has a verified premium account", username);
if (plugin.getCore().getApiConnector().hasJoinedServer(session, serverId, fromPlayer.getAddress())) {
plugin.getLogger().log(Level.INFO, "Player {0} has a verified premium account", username);
session.setVerified(true);
setPremiumUUID(session.getUuid());
@@ -110,7 +107,7 @@ public class VerifyResponseTask implements Runnable {
}
}
private boolean checkVerifyToken(BukkitLoginSession session, PrivateKey privateKey) {
private boolean checkVerifyToken(BukkitLoginSession session, Key privateKey) {
byte[] requestVerify = session.getVerifyToken();
//encrypted verify token
byte[] responseVerify = packetEvent.getPacket().getByteArrays().read(1);
@@ -128,11 +125,12 @@ public class VerifyResponseTask implements Runnable {
}
//try to get the networkManager from ProtocolLib
private Object getNetworkManager() throws IllegalAccessException, NoSuchFieldException {
private Object getNetworkManager() throws IllegalAccessException, NoSuchFieldException, ClassNotFoundException {
Object injectorContainer = TemporaryPlayerFactory.getInjectorFromPlayer(fromPlayer);
//ChannelInjector
Injector rawInjector = FuzzyReflection.getFieldValue(injectorContainer, Injector.class, true);
Class<?> injectorClass = Class.forName("com.comphenix.protocol.injector.netty.Injector");
Object rawInjector = FuzzyReflection.getFieldValue(injectorContainer, injectorClass, true);
return FieldUtils.readField(rawInjector, "networkManager", true);
}
@@ -172,7 +170,6 @@ public class VerifyResponseTask implements Runnable {
PacketContainer kickPacket = protocolManager.createPacket(PacketType.Login.Server.DISCONNECT);
kickPacket.getChatComponents().write(0, WrappedChatComponent.fromText(reason));
try {
//send kick packet at login state
//the normal event.getPlayer.kickPlayer(String) method does only work at play state
@@ -180,7 +177,7 @@ public class VerifyResponseTask implements Runnable {
//tell the server that we want to close the connection
player.kickPlayer("Disconnect");
} catch (InvocationTargetException ex) {
plugin.getLogger().log(Level.SEVERE, "Error sending kickpacket", ex);
plugin.getLogger().log(Level.SEVERE, "Error sending kick packet", ex);
}
}

View File

@@ -2,19 +2,20 @@ 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.core.shared.JoinManagement;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.core.shared.JoinManagement;
import java.net.InetSocketAddress;
import org.bukkit.command.CommandSender;
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 extends JoinManagement<Player, ProtocolLoginSource> implements Listener {
public class ProtocolSupportListener extends JoinManagement<Player, CommandSender, ProtocolLoginSource>
implements Listener {
private final FastLoginBukkit plugin;
@@ -26,7 +27,6 @@ public class ProtocolSupportListener extends JoinManagement<Player, ProtocolLogi
@EventHandler
public void onLoginStart(PlayerLoginStartEvent loginStartEvent) {
plugin.setServerStarted();
if (loginStartEvent.isLoginDenied() || plugin.getCore().getAuthPluginHook() == null) {
return;
}
@@ -35,7 +35,7 @@ public class ProtocolSupportListener extends JoinManagement<Player, ProtocolLogi
InetSocketAddress address = loginStartEvent.getAddress();
//remove old data every time on a new login in order to keep the session only for one person
plugin.getSessions().remove(address.toString());
plugin.getLoginSessions().remove(address.toString());
super.onLogin(username, new ProtocolLoginSource(loginStartEvent));
}
@@ -43,13 +43,10 @@ public class ProtocolSupportListener extends JoinManagement<Player, ProtocolLogi
@EventHandler
public void onPropertiesResolve(PlayerPropertiesResolveEvent propertiesResolveEvent) {
InetSocketAddress address = propertiesResolveEvent.getAddress();
BukkitLoginSession session = plugin.getSessions().get(address.toString());
BukkitLoginSession session = plugin.getLoginSessions().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);
}
}
@@ -59,10 +56,10 @@ public class ProtocolSupportListener extends JoinManagement<Player, ProtocolLogi
source.setOnlineMode();
String ip = source.getAddress().getAddress().getHostAddress();
plugin.getCore().getPendingLogins().put(ip + username, new Object());
plugin.getCore().getPendingLogin().put(ip + username, new Object());
BukkitLoginSession playerSession = new BukkitLoginSession(username, null, null, registered, profile);
plugin.getSessions().put(source.getAddress().toString(), playerSession);
plugin.getLoginSessions().put(source.getAddress().toString(), playerSession);
if (plugin.getConfig().getBoolean("premiumUuid")) {
source.getLoginStartEvent().setUseOnlineModeUUID(true);
}
@@ -71,6 +68,6 @@ public class ProtocolSupportListener extends JoinManagement<Player, ProtocolLogi
@Override
public void startCrackedSession(ProtocolLoginSource source, PlayerProfile profile, String username) {
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.getSessions().put(source.getAddress().toString(), loginSession);
plugin.getLoginSessions().put(source.getAddress().toString(), loginSession);
}
}

View File

@@ -26,19 +26,24 @@ public class DelayedAuthHook implements Runnable {
@Override
public void run() {
boolean hookFound = registerHooks();
boolean hookFound = plugin.getCore().getAuthPluginHook() != null || registerHooks();
if (plugin.isBungeeCord()) {
plugin.getLogger().info("BungeeCord setting detected. No auth plugin is required");
} else if (!hookFound) {
plugin.getLogger().warning("No auth plugin were found by this plugin "
+ "(other plugins could hook into this after the intialization of this plugin)"
+ "and bungeecord is deactivated. "
+ "(other plugins could hook into this after the initialization of this plugin)"
+ "and BungeeCord is deactivated. "
+ "Either one or both of the checks have to pass in order to use this plugin");
}
if (hookFound) {
plugin.setServerStarted();
}
}
private boolean registerHooks() {
AuthPlugin<Player> authPluginHook = null;
try {
@SuppressWarnings("unchecked")
List<Class<? extends AuthPlugin<Player>>> supportedHooks = Lists.newArrayList(AuthMeHook.class
@@ -67,9 +72,8 @@ public class DelayedAuthHook implements Runnable {
if (plugin.getCore().getAuthPluginHook() == null) {
plugin.getCore().setAuthPluginHook(authPluginHook);
plugin.setServerStarted();
}
return true;
}
}

View File

@@ -2,132 +2,67 @@ 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.core.AuthStorage;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.ForceLoginManagement;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.metadata.FixedMetadataValue;
public class ForceLoginTask implements Runnable {
public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> {
private final FastLoginBukkit plugin;
private final Player player;
public ForceLoginTask(FastLoginBukkit plugin, Player player) {
this.plugin = plugin;
this.player = player;
public ForceLoginTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player) {
super(core, player);
}
@Override
public void run() {
if (!isOnlineThreadSafe()) {
return;
}
//remove the bungeecord identifier if there is ones
String id = '/' + player.getAddress().getAddress().getHostAddress() + ':' + player.getAddress().getPort();
BukkitLoginSession session = plugin.getSessions().remove(id);
if (session == null) {
return;
}
session = core.getPlugin().getLoginSessions().remove(id);
AuthStorage storage = plugin.getCore().getStorage();
PlayerProfile playerProfile = session.getProfile();
//blacklist this target player for BungeeCord Id brute force attacks
FastLoginBukkit plugin = core.getPlugin();
player.setMetadata(core.getPlugin().getName(), new FixedMetadataValue(plugin, true));
//check if it's the same player as we checked before
if (session.isVerified() && player.getName().equals(session.getUsername())) {
//premium player
AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook();
if (authPlugin == null) {
//maybe only bungeecord plugin
sendSuccessNotification();
} else {
boolean success = false;
if (isOnlineThreadSafe()) {
if (plugin.getConfig().getBoolean("autoLogin")) {
if (session.needsRegistration()) {
success = forceRegister(authPlugin, player);
} else {
success = forceLogin(authPlugin, player);
}
} else {
success = true;
}
}
if (success) {
//update only on success to prevent corrupt data
if (playerProfile != null) {
playerProfile.setUuid(session.getUuid());
//save cracked players too
playerProfile.setPremium(true);
storage.save(playerProfile);
}
sendSuccessNotification();
}
}
} else {
//cracked player
if (playerProfile != null) {
playerProfile.setUuid(null);
playerProfile.setPremium(false);
storage.save(playerProfile);
}
}
super.run();
}
private boolean forceRegister(AuthPlugin<Player> authPlugin, Player player) {
plugin.getLogger().log(Level.FINE, "Register player {0}", player.getName());
String generatedPassword = plugin.getCore().getPasswordGenerator().getRandomPassword(player);
boolean success = authPlugin.forceRegister(player, generatedPassword);
String message = plugin.getCore().getMessage("auto-register");
if (success && message != null) {
message = message.replace("%password", generatedPassword);
player.sendMessage(message);
}
return success;
}
private boolean forceLogin(AuthPlugin<Player> authPlugin, Player player) {
plugin.getLogger().log(Level.FINE, "Logging player {0} in", player.getName());
boolean success = authPlugin.forceLogin(player);
String message = plugin.getCore().getMessage("auto-login");
if (success && message != null) {
player.sendMessage(message);
}
return success;
}
private void sendSuccessNotification() {
if (plugin.isBungeeCord()) {
@Override
public void onForceActionSuccess(LoginSession session) {
if (core.getPlugin().isBungeeCord()) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
dataOutput.writeUTF("SUCCESS");
player.sendPluginMessage(plugin, plugin.getName(), dataOutput.toByteArray());
player.sendPluginMessage(core.getPlugin(), core.getPlugin().getName(), dataOutput.toByteArray());
}
}
private boolean isOnlineThreadSafe() {
//the playerlist isn't thread-safe
Future<Boolean> onlineFuture = Bukkit.getScheduler().callSyncMethod(plugin, player::isOnline);
@Override
public String getName(Player player) {
return player.getName();
}
@Override
public boolean isOnline(Player player) {
try {
return onlineFuture.get();
//the playerlist isn't thread-safe
return Bukkit.getScheduler().callSyncMethod(core.getPlugin(), player::isOnline).get();
} catch (InterruptedException | ExecutionException ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to perform thread-safe online check", ex);
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to perform thread-safe online check", ex);
return false;
}
}
@Override
public boolean isOnlineMode() {
return session.isVerified() && player.getName().equals(session.getUsername());
}
}

View File

@@ -1,10 +1,10 @@
# project informations for Bukkit in order to register our plugin with all it components
# project data for Bukkit in order to register our plugin with all it components
# ${-} are variables from Maven (pom.xml) which will be replaced after the build
name: ${project.parent.name}
version: ${project.version}
version: ${project.version}-git${git.commit.id}
main: ${project.groupId}.${project.artifactId}.${project.name}
# meta informations for plugin managers
# meta data for plugin managers
authors: [games647, 'https://github.com/games647/FastLogin/graphs/contributors']
description: |
${project.description}
@@ -18,6 +18,7 @@ softdepend:
# We depend either ProtocolLib or ProtocolSupport
- ProtocolSupport
- ProtocolLib
- PlaceholderAPI
commands:
${project.parent.name}:
@@ -32,11 +33,6 @@ commands:
usage: /<command> [player]
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:
description: 'Label themselves as premium'
@@ -54,4 +50,4 @@ permissions:
${project.artifactId}.command.cracked.other:
description: 'Label others as cracked'
children:
${project.artifactId}.command.cracked: true
${project.artifactId}.command.cracked: true

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.9</version>
<version>1.10</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -19,8 +19,8 @@
<repositories>
<!--BungeeCord with also the part outside the API-->
<repository>
<id>myplayplanet-REPO</id>
<url>http://maven.myplayplanet.net/</url>
<id>luck-repo</id>
<url>https://ci.lucko.me/plugin/repository/everything</url>
</repository>
<repository>
@@ -40,14 +40,20 @@
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId>
<version>1.8-SNAPSHOT</version>
<version>1.12-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.vik1395</groupId>
<artifactId>BungeeAuth</artifactId>
<version>1.3.1</version>
<version>1.4</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View File

@@ -1,87 +0,0 @@
package com.github.games647.fastlogin.bungee;
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.util.List;
import java.util.Map;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
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<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, Configuration config) {
super(generateConfigMap(config));
this.plugin = plugin;
}
@Override
public File getDataFolder() {
return plugin.getDataFolder();
}
@Override
public Logger getLogger() {
return plugin.getLogger();
}
@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();
}
@Override
public void loadMessages() {
try {
plugin.saveDefaultFile("messages.yml");
ConfigurationProvider configProvider = ConfigurationProvider.getProvider(YamlConfiguration.class);
Configuration defaults = configProvider.load(getClass().getResourceAsStream("/messages.yml"));
File messageFile = new File(getDataFolder(), "messages.yml");
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 MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests) {
return new MojangApiBungee(logger, addresses, requests);
}
}

View File

@@ -1,20 +1,29 @@
package com.github.games647.fastlogin.bungee;
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.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.MojangApiConnector;
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
import com.google.common.collect.Maps;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.io.Reader;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.concurrent.ThreadFactory;
import java.util.function.Function;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
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.plugin.Plugin;
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;
@@ -22,42 +31,24 @@ import net.md_5.bungee.config.YamlConfiguration;
/**
* BungeeCord version of FastLogin. This plugin keeps track on online mode connections.
*/
public class FastLoginBungee extends Plugin {
public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSender> {
private final ConcurrentMap<PendingConnection, BungeeLoginSession> session = Maps.newConcurrentMap();
private BungeeCore core;
private Configuration config;
private FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core;
@Override
public void onEnable() {
saveDefaultFile("config.yml");
try {
File configFile = new File(getDataFolder(), "config.yml");
ConfigurationProvider provider = ConfigurationProvider.getProvider(YamlConfiguration.class);
Configuration defaults = provider.load(getResourceAsStream("config.yml"));
config = provider.load(configFile, defaults);
core = new BungeeCore(this, config);
if (!core.setupDatabase()) {
return;
}
} catch (IOException ioExc) {
getLogger().log(Level.SEVERE, "Error loading config. Disabling plugin...", ioExc);
core = new FastLoginCore<>(this);
core.load();
if (!core.setupDatabase()) {
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());
@@ -71,55 +62,14 @@ public class FastLoginBungee extends Plugin {
}
}
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() {
public FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> getCore() {
return core;
}
@Deprecated
public void setAuthPluginHook(BungeeAuthPlugin authPlugin) {
core.setAuthPluginHook(authPlugin);
}
public Configuration getConfig() {
return config;
}
public ConcurrentMap<PendingConnection, BungeeLoginSession> getSession() {
return session;
}
/**
* Get the auth plugin hook for BungeeCord
*
* @return the auth hook for BungeeCord. null if none found
*/
@Deprecated
public BungeeAuthPlugin getBungeeAuthPlugin() {
return (BungeeAuthPlugin) core.getAuthPluginHook();
}
private void registerHook() {
Plugin plugin = getProxy().getPluginManager().getPlugin("BungeeAuth");
if (plugin != null) {
@@ -127,4 +77,40 @@ public class FastLoginBungee extends Plugin {
getLogger().info("Hooked into BungeeAuth");
}
}
@Override
public String getName() {
return getDescription().getName();
}
@Override
public Map<String, Object> loadYamlFile(Reader reader) {
ConfigurationProvider configProvider = ConfigurationProvider.getProvider(YamlConfiguration.class);
Configuration config = configProvider.load(reader);
return config.getKeys().stream()
.filter(key -> config.get(key) != null)
.collect(Collectors.toMap(Function.identity(), config::get));
}
@Override
public void sendMessage(CommandSender receiver, String message) {
receiver.sendMessage(TextComponent.fromLegacyText(message));
}
@Override
public String translateColorCodes(char colorChar, String rawMessage) {
return ChatColor.translateAlternateColorCodes(colorChar, rawMessage);
}
@Override
@SuppressWarnings("deprecation")
public ThreadFactory getThreadFactory() {
return new GroupedThreadFactory(this, getName());
}
@Override
public MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests
, Map<String, Integer> proxies) {
return new MojangApiBungee(logger, addresses, requests, proxies);
}
}

View File

@@ -1,91 +0,0 @@
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

@@ -3,15 +3,17 @@ package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.shared.MojangApiConnector;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import net.md_5.bungee.BungeeCord;
public class MojangApiBungee extends MojangApiConnector {
public MojangApiBungee(Logger logger, List<String> localAddresses, int rateLimit) {
super(logger, localAddresses, rateLimit);
public MojangApiBungee(Logger logger, List<String> localAddresses, int rateLimit, Map<String, Integer> proxies) {
super(logger, localAddresses, rateLimit, proxies);
}
@Override
@@ -29,12 +31,12 @@ public class MojangApiBungee extends MojangApiConnector {
if ("null".equals(id)) {
return null;
}
return id;
}
@Override
public boolean hasJoinedServer(LoginSession session, String serverId) {
public boolean hasJoinedServer(LoginSession session, String serverId, InetSocketAddress ip) {
//this is not needed in Bungee
throw new UnsupportedOperationException("Not supported");
}

View File

@@ -2,21 +2,13 @@ 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;
import java.util.Random;
import java.util.logging.Level;
import me.vik1395.BungeeAuth.ListenerClass;
import me.vik1395.BungeeAuth.Main;
import me.vik1395.BungeeAuth.Password.PasswordHandler;
import me.vik1395.BungeeAuth.Tables;
import me.vik1395.BungeeAuthAPI.RequestHandler;
import net.md_5.bungee.api.connection.ProxiedPlayer;
/**
* Github: https://github.com/MatteCarra/BungeeAuth
* Github: https://github.com/vik1395/BungeeAuth-Minecraft
*
* Project page:
*
@@ -24,88 +16,25 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
*/
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();
private final RequestHandler requestHandler = new RequestHandler();
@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
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[]{playerName, "online"};
try {
callProtected("setStatus", parameterTypes, arguments);
ListenerClass.movePlayer(player, false);
//proparly not thread-safe
ListenerClass.prelogin.get(playerName).cancel();
} catch (Exception ex) {
Main.plugin.getLogger().log(Level.SEVERE, "Error force loging in player", ex);
return false;
}
return true;
return requestHandler.forceLogin(playerName);
}
@Override
public boolean isRegistered(String playerName) throws Exception {
//https://github.com/MatteCarra/BungeeAuth/blob/master/src/me/vik1395/BungeeAuth/Register.java#L46
//renamed t to databaseConnection
return databaseConnection.checkPlayerEntry(playerName);
return requestHandler.isRegistered(playerName);
}
@Override
public boolean forceRegister(ProxiedPlayer player, String password) {
//https://github.com/MatteCarra/BungeeAuth/blob/master/src/me/vik1395/BungeeAuth/Register.java#L102
PasswordHandler ph = new PasswordHandler();
Random rand = new Random();
int maxp = 7; //Total Password Hashing methods.
Date dNow = new Date();
SimpleDateFormat ft = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
String Pw = password;
String pType = "" + rand.nextInt(maxp + 1);
String regdate = ft.format(dNow);
//https://github.com/MatteCarra/BungeeAuth/blob/master/src/me/vik1395/BungeeAuth/Register.java#L60
String lastip = player.getAddress().getAddress().getHostAddress();
String lastseen = regdate;
String hash = ph.newHash(Pw, pType);
//creates a new SQL entry with the player's details.
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);
} catch (Exception ex) {
Main.plugin.getLogger().log(Level.SEVERE, "[BungeeAuth] Error when creating a new player in the Database", ex);
return false;
}
return true;
}
//pail ;(
private void callProtected(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception {
Class<Tables> tableClass = Tables.class;
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);
return requestHandler.forceRegister(player, password);
}
}

View File

@@ -1,20 +0,0 @@
package com.github.games647.fastlogin.bungee.hooks;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import net.md_5.bungee.api.connection.ProxiedPlayer;
/**
* @deprecated please use com.github.games647.fastlogin.core.hooks.AuthPlugin<net.md_5.bungee.api.connection.ProxiedPlayer>
*/
@Deprecated
public interface BungeeAuthPlugin extends AuthPlugin<ProxiedPlayer> {
@Override
boolean forceLogin(ProxiedPlayer player);
@Override
boolean isRegistered(String playerName) throws Exception;
@Override
boolean forceRegister(ProxiedPlayer player, String password);
}

View File

@@ -14,6 +14,7 @@ import java.util.logging.Level;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PreLoginEvent;
@@ -47,24 +48,23 @@ public class PlayerConnectionListener implements Listener {
preLoginEvent.registerIntent(plugin);
PendingConnection connection = preLoginEvent.getConnection();
AsyncPremiumCheck asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection);
Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection);
ProxyServer.getInstance().getScheduler().runAsync(plugin, asyncPremiumCheck);
}
@EventHandler(priority = EventPriority.LOW)
@EventHandler(priority = EventPriority.LOWEST)
public void onLogin(LoginEvent loginEvent) {
if (loginEvent.isCancelled()) {
return;
}
//use the loginevent instead of the postlogin event in order to send the loginsuccess packet to the client
//use the login event instead of the postlogin event in order to send the loginsuccess packet to the client
//with the offline uuid this makes it possible to set the skin then
PendingConnection connection = loginEvent.getConnection();
String username = connection.getName();
if (connection.isOnlineMode()) {
String ip = connection.getAddress().getAddress().getHostAddress();
plugin.getCore().getPendingLogins().remove(ip + username);
InitialHandler initialHandler = (InitialHandler) connection;
String username = initialHandler.getLoginRequest().getData();
if (connection.isOnlineMode()) {
LoginSession session = plugin.getSession().get(connection);
session.setUuid(connection.getUniqueId());
@@ -72,8 +72,7 @@ public class PlayerConnectionListener implements Listener {
playerProfile.setUuid(connection.getUniqueId());
//bungeecord will do this automatically so override it on disabled option
InitialHandler initialHandler = (InitialHandler) connection;
if (!plugin.getConfig().getBoolean("premiumUuid")) {
if (!plugin.getCore().getConfig().get("premiumUuid", true)) {
try {
UUID offlineUUID = UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(Charsets.UTF_8));
@@ -87,7 +86,7 @@ public class PlayerConnectionListener implements Listener {
}
}
if (!plugin.getConfig().getBoolean("forwardSkin")) {
if (!plugin.getCore().getConfig().get("forwardSkin", true)) {
//this is null on offline mode
LoginResult loginProfile = initialHandler.getLoginProfile();
if (loginProfile != null) {
@@ -100,7 +99,9 @@ public class PlayerConnectionListener implements Listener {
@EventHandler
public void onServerConnected(ServerConnectedEvent serverConnectedEvent) {
ProxiedPlayer player = serverConnectedEvent.getPlayer();
ForceLoginTask loginTask = new ForceLoginTask(plugin, player, serverConnectedEvent.getServer());
Server server = serverConnectedEvent.getServer();
Runnable loginTask = new ForceLoginTask(plugin.getCore(), player, server);
ProxyServer.getInstance().getScheduler().runAsync(plugin, loginTask);
}

View File

@@ -4,11 +4,13 @@ import com.github.games647.fastlogin.bungee.BungeeLoginSession;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.tasks.AsyncToggleMessage;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import java.util.Arrays;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
@@ -47,30 +49,34 @@ public class PluginMessageListener implements Listener {
}
private void readMessage(ProxiedPlayer forPlayer, byte[] data) {
FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core = plugin.getCore();
ByteArrayDataInput dataInput = ByteStreams.newDataInput(data);
String subchannel = dataInput.readUTF();
if ("ON".equals(subchannel)) {
if ("SUCCESS".equals(subchannel)) {
onSuccessMessage(forPlayer);
} else if ("ON".equals(subchannel)) {
String playerName = dataInput.readUTF();
boolean isPlayerSender = dataInput.readBoolean();
if (playerName.equals(forPlayer.getName()) && plugin.getConfig().getBoolean("premium-warning")
&& !plugin.getCore().getPendingConfirms().contains(forPlayer.getUniqueId())) {
String message = plugin.getCore().getMessage("premium-warning");
if (playerName.equals(forPlayer.getName()) && plugin.getCore().getConfig().get("premium-warning", true)
&& !core.getPendingConfirms().contains(forPlayer.getUniqueId())) {
String message = core.getMessage("premium-warning");
forPlayer.sendMessage(TextComponent.fromLegacyText(message));
plugin.getCore().getPendingConfirms().add(forPlayer.getUniqueId());
core.getPendingConfirms().add(forPlayer.getUniqueId());
return;
}
plugin.getCore().getPendingConfirms().remove(forPlayer.getUniqueId());
AsyncToggleMessage task = new AsyncToggleMessage(plugin.getCore(), forPlayer, playerName, true);
core.getPendingConfirms().remove(forPlayer.getUniqueId());
Runnable task = new AsyncToggleMessage(core, forPlayer, playerName, true, isPlayerSender);
ProxyServer.getInstance().getScheduler().runAsync(plugin, task);
} else if ("OFF".equals(subchannel)) {
String playerName = dataInput.readUTF();
boolean isPlayerSender = dataInput.readBoolean();
AsyncToggleMessage task = new AsyncToggleMessage(plugin.getCore(), forPlayer, playerName, false);
Runnable task = new AsyncToggleMessage(core, forPlayer, playerName, false, isPlayerSender);
ProxyServer.getInstance().getScheduler().runAsync(plugin, task);
} else if ("SUCCESS".equals(subchannel)) {
onSuccessMessage(forPlayer);
}
}
}
private void onSuccessMessage(ProxiedPlayer forPlayer) {

View File

@@ -6,11 +6,14 @@ import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.core.shared.JoinManagement;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.AsyncEvent;
import net.md_5.bungee.connection.InitialHandler;
public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, BungeeLoginSource> implements Runnable {
public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, CommandSender, BungeeLoginSource>
implements Runnable {
private final FastLoginBungee plugin;
private final AsyncEvent<?> preLoginEvent;
@@ -29,7 +32,8 @@ public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, BungeeLogin
public void run() {
plugin.getSession().remove(connection);
String username = connection.getName();
InitialHandler initialHandler = (InitialHandler) connection;
String username = initialHandler.getLoginRequest().getData();
try {
super.onLogin(username, new BungeeLoginSource(connection));
} finally {
@@ -43,7 +47,7 @@ public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, BungeeLogin
plugin.getSession().put(source.getConnection(), new BungeeLoginSession(username, registered, profile));
String ip = source.getAddress().getAddress().getHostAddress();
plugin.getCore().getPendingLogins().put(ip + username, new Object());
plugin.getCore().getPendingLogin().put(ip + username, new Object());
}
@Override

View File

@@ -1,23 +1,29 @@
package com.github.games647.fastlogin.bungee.tasks;
import com.github.games647.fastlogin.bungee.BungeeCore;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
public class AsyncToggleMessage implements Runnable {
private final BungeeCore core;
private final ProxiedPlayer fromPlayer;
private final FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core;
private final ProxiedPlayer sender;
private final String targetPlayer;
private final boolean toPremium;
private final boolean isPlayerSender;
public AsyncToggleMessage(BungeeCore core, ProxiedPlayer fromPlayer, String targetPlayer, boolean toPremium) {
public AsyncToggleMessage(FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core,
ProxiedPlayer sender, String playerName, boolean toPremium, boolean playerSender) {
this.core = core;
this.fromPlayer = fromPlayer;
this.targetPlayer = targetPlayer;
this.sender = sender;
this.targetPlayer = playerName;
this.toPremium = toPremium;
this.isPlayerSender = playerSender;
}
@Override
@@ -33,25 +39,35 @@ public class AsyncToggleMessage implements Runnable {
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")));
sendMessage("not-premium");
return;
}
playerProfile.setPremium(false);
playerProfile.setUuid(null);
core.getStorage().save(playerProfile);
fromPlayer.sendMessage(TextComponent.fromLegacyText(core.getMessage("remove-premium")));
sendMessage("remove-premium");
}
private void activatePremium() {
PlayerProfile playerProfile = core.getStorage().loadProfile(targetPlayer);
if (playerProfile.isPremium()) {
fromPlayer.sendMessage(TextComponent.fromLegacyText(core.getMessage("already-exists")));
sendMessage("already-exists");
return;
}
playerProfile.setPremium(true);
core.getStorage().save(playerProfile);
fromPlayer.sendMessage(TextComponent.fromLegacyText(core.getMessage("add-premium")));
sendMessage("add-premium");
}
private void sendMessage(String localeId) {
String message = core.getMessage(localeId);
if (isPlayerSender) {
sender.sendMessage(TextComponent.fromLegacyText(message));
} else {
CommandSender console = ProxyServer.getInstance().getConsole();
console.sendMessage(TextComponent.fromLegacyText(message));
}
}
}

View File

@@ -2,119 +2,70 @@ 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.core.PlayerProfile;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.ForceLoginManagement;
import com.github.games647.fastlogin.core.shared.LoginSession;
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.CommandSender;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
public class ForceLoginTask implements Runnable {
public class ForceLoginTask extends ForceLoginManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> {
private final FastLoginBungee plugin;
private final ProxiedPlayer player;
private final Server server;
public ForceLoginTask(FastLoginBungee plugin, ProxiedPlayer player, Server server) {
this.plugin = plugin;
this.player = player;
public ForceLoginTask(FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core,
ProxiedPlayer player, Server server) {
super(core, player);
this.server = server;
}
@Override
public void run() {
try {
PendingConnection pendingConnection = player.getPendingConnection();
BungeeLoginSession session = plugin.getSession().get(pendingConnection);
PendingConnection pendingConnection = player.getPendingConnection();
session = core.getPlugin().getSession().get(pendingConnection);
if (session == null || !player.isConnected()) {
plugin.getLogger().log(Level.FINE, "Invalid session player {0} propaly left the server", player);
return;
}
PlayerProfile playerProfile = session.getProfile();
//force login only on success
if (pendingConnection.isOnlineMode()) {
boolean autoRegister = session.needsRegistration();
//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);
} else if (session.needsRegistration()) {
forceRegister(session, authPlugin);
} else if (authPlugin.forceLogin(player)) {
forceLogin(session, authPlugin);
}
} else {
//cracked player
if (!session.isAlreadySaved()) {
playerProfile.setPremium(false);
plugin.getCore().getStorage().save(playerProfile);
session.setAlreadySaved(true);
}
}
} catch (Exception ex) {
plugin.getLogger().log(Level.INFO, "ERROR ON FORCE LOGIN", ex);
}
}
private void forceRegister(BungeeLoginSession session, AuthPlugin<ProxiedPlayer> authPlugin) {
if (session.isAlreadyLogged()) {
sendBukkitLoginNotification(true);
if (session == null) {
return;
}
session.setAlreadyLogged(true);
super.run();
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(TextComponent.fromLegacyText(message));
}
if (!isOnlineMode()) {
session.setAlreadySaved(true);
}
}
private void forceLogin(BungeeLoginSession session, AuthPlugin<ProxiedPlayer> authPlugin) {
@Override
public boolean forceLogin(ProxiedPlayer player) {
if (session.isAlreadyLogged()) {
sendBukkitLoginNotification(false);
return;
return true;
}
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(TextComponent.fromLegacyText(message));
}
}
return super.forceLogin(player);
}
private void sendBukkitLoginNotification(boolean autoRegister) {
@Override
public boolean forceRegister(ProxiedPlayer player) {
if (session.isAlreadyLogged()) {
return true;
}
return super.forceRegister(player);
}
@Override
public void onForceActionSuccess(LoginSession session) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
//subchannel name
if (autoRegister) {
//sub channel name
if (session.needsRegistration()) {
dataOutput.writeUTF("AUTO_REGISTER");
} else {
dataOutput.writeUTF("AUTO_LOGIN");
@@ -124,12 +75,27 @@ public class ForceLoginTask implements Runnable {
dataOutput.writeUTF(player.getName());
//proxy identifier to check if it's a acceptable proxy
UUID proxyId = UUID.fromString(plugin.getProxy().getConfig().getUuid());
UUID proxyId = UUID.fromString(core.getPlugin().getProxy().getConfig().getUuid());
dataOutput.writeLong(proxyId.getMostSignificantBits());
dataOutput.writeLong(proxyId.getLeastSignificantBits());
if (server != null) {
server.sendData(plugin.getDescription().getName(), dataOutput.toByteArray());
server.sendData(core.getPlugin().getName(), dataOutput.toByteArray());
}
}
@Override
public String getName(ProxiedPlayer player) {
return player.getName();
}
@Override
public boolean isOnline(ProxiedPlayer player) {
return player.isConnected();
}
@Override
public boolean isOnlineMode() {
return player.getPendingConnection().isOnlineMode();
}
}

View File

@@ -1,16 +1,16 @@
# project informations for BungeeCord
# project data for BungeeCord
# This file will be prioritised over plugin.yml which can be also used for Bungee
# This make it easy to combine BungeeCord and Bukkit support in one plugin
name: ${project.parent.name}
# ${-} will be automatically replaced by Maven
main: ${project.groupId}.${project.artifactId}.${project.name}
version: ${project.version}
version: ${project.version}-git${git.commit.id}
author: games647, http://github.com/games647/FastLogin/graphs/contributors
softdepend:
softDepends:
# BungeeCord auth plugins
- BungeeAuth
description: |
${project.description}
${project.description}

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.9</version>
<version>1.10</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -19,16 +19,16 @@
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.5.0</version>
<version>2.6.3</version>
</dependency>
<!--Logging framework implements slf4j which is required by hikari-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.21</version>
<version>1.7.22</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>

View File

@@ -1,6 +1,7 @@
package com.github.games647.fastlogin.core;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
@@ -9,46 +10,60 @@ 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.Properties;
import java.util.UUID;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import javax.sql.DataSource;
public class AuthStorage {
private static final String PREMIUM_TABLE = "premium";
private final FastLoginCore<?> core;
private final FastLoginCore<?, ?, ?> core;
private final HikariDataSource dataSource;
//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) {
public AuthStorage(FastLoginCore<?, ?, ?> core, String driver, String host, int port, String databasePath
, String user, String pass, boolean useSSL) {
this.core = core;
HikariConfig databaseConfig = new HikariConfig();
databaseConfig.setUsername(user);
databaseConfig.setPassword(pass);
databaseConfig.setDriverClassName(driver);
databaseConfig.setThreadFactory(core.getThreadFactory());
HikariConfig config = new HikariConfig();
config.setUsername(user);
config.setPassword(pass);
config.setDriverClassName(driver);
databasePath = databasePath.replace("{pluginDir}", core.getDataFolder().getAbsolutePath());
//a try to fix https://www.spigotmc.org/threads/fastlogin.101192/page-26#post-1874647
Properties properties = new Properties();
properties.setProperty("date_string_format", "yyyy-MM-dd HH:mm:ss");
properties.setProperty("useSSL", String.valueOf(useSSL));
config.setDataSourceProperties(properties);
ThreadFactory platformThreadFactory = core.getPlugin().getThreadFactory();
if (platformThreadFactory != null) {
config.setThreadFactory(new ThreadFactoryBuilder()
.setNameFormat(core.getPlugin().getName() + " Database Pool Thread #%1$d")
//Hikari create daemons by default
.setDaemon(true)
.setThreadFactory(platformThreadFactory)
.build());
}
databasePath = databasePath.replace("{pluginDir}", core.getPlugin().getDataFolder().getAbsolutePath());
String jdbcUrl = "jdbc:";
if (driver.contains("sqlite")) {
jdbcUrl += "sqlite" + "://" + databasePath;
databaseConfig.setConnectionTestQuery("SELECT 1");
config.setConnectionTestQuery("SELECT 1");
} else {
jdbcUrl += "mysql" + "://" + host + ':' + port + '/' + databasePath;
}
databaseConfig.setJdbcUrl(jdbcUrl);
this.dataSource = new HikariDataSource(databaseConfig);
config.setJdbcUrl(jdbcUrl);
this.dataSource = new HikariDataSource(config);
}
public HikariDataSource getDataSource() {
public DataSource getDataSource() {
return dataSource;
}
@@ -68,7 +83,7 @@ public class AuthStorage {
+ "LastLogin TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
//the premium shouldn't steal the cracked account by changing the name
+ "UNIQUE (Name) "
+ ")";
+ ')';
if (dataSource.getJdbcUrl().contains("sqlite")) {
createDataStmt = createDataStmt.replace("AUTO_INCREMENT", "AUTOINCREMENT");
@@ -126,15 +141,13 @@ public class AuthStorage {
boolean premium = resultSet.getBoolean(4);
String lastIp = resultSet.getString(5);
long lastLogin = resultSet.getTimestamp(6, calendar).getTime();
PlayerProfile playerProfile = new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
return playerProfile;
long lastLogin = resultSet.getTimestamp(6).getTime();
return new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
} else {
PlayerProfile crackedProfile = new PlayerProfile(null, name, false, "");
return crackedProfile;
return new PlayerProfile(null, name, false, "");
}
} catch (SQLException sqlEx) {
core.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
} finally {
closeQuietly(con);
closeQuietly(loadStmt);
@@ -160,12 +173,11 @@ public class AuthStorage {
String name = resultSet.getString(3);
boolean premium = resultSet.getBoolean(4);
String lastIp = resultSet.getString(5);
long lastLogin = resultSet.getTimestamp(6, calendar).getTime();
PlayerProfile playerProfile = new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
return playerProfile;
long lastLogin = resultSet.getTimestamp(6).getTime();
return new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
}
} catch (SQLException sqlEx) {
core.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
} finally {
closeQuietly(con);
closeQuietly(loadStmt);
@@ -226,7 +238,7 @@ public class AuthStorage {
return true;
} catch (SQLException ex) {
core.getLogger().log(Level.SEVERE, "Failed to save playerProfile", ex);
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to save playerProfile", ex);
} finally {
closeQuietly(con);
closeQuietly(updateStmt);
@@ -246,7 +258,7 @@ public class AuthStorage {
try {
closeable.close();
} catch (Exception closeEx) {
core.getLogger().log(Level.SEVERE, "Failed to close connection", closeEx);
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to close connection", closeEx);
}
}
}

View File

@@ -5,7 +5,6 @@ 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.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -36,18 +35,18 @@ public class BalancedSSLFactory extends SSLSocketFactory {
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoclose) throws IOException {
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return oldFactory.createSocket(host, port, getNextLocalAddress(), 0);
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
public Socket createSocket(String host, int port) throws IOException {
return oldFactory.createSocket(host, port, getNextLocalAddress(), 0);
}
@Override
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort)
throws IOException, UnknownHostException {
throws IOException {
//default
return oldFactory.createSocket(host, port, localAddress, localPort);
}

View File

@@ -1,14 +1,14 @@
package com.github.games647.fastlogin.core;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import com.google.common.base.Ticker;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.RemovalListener;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
/**
* Represents a Guava CacheBuilder that is compatible with both Guava 10 (Minecraft 1.7.X) and 13
*/
@@ -54,7 +54,7 @@ public class CompatibleCacheBuilder<K, V> {
* @param concurrencyLevel New concurrency level
* @return This for chaining
*
* @throws IllegalArgumentException if {@code concurrencyLevel} is nonpositive
* @throws IllegalArgumentException if {@code concurrencyLevel} is non-positive
* @throws IllegalStateException if a concurrency level was already set
*/
public CompatibleCacheBuilder<K, V> concurrencyLevel(int concurrencyLevel) {
@@ -76,7 +76,7 @@ public class CompatibleCacheBuilder<K, V> {
* <p>
* Expired entries may be counted by {@link com.google.common.cache.Cache#size Cache.size()}, but will never be
* visible to read or write operations. Expired entries are currently cleaned up during write operations, or during
* occasional read operations in the absense of writes; though this behavior may change in the future.
* occasional read operations in the absence of writes; though this behavior may change in the future.
*
* @param duration the length of time after an entry is last accessed that it should be automatically removed
* @param unit the unit that {@code duration} is expressed in
@@ -102,7 +102,7 @@ public class CompatibleCacheBuilder<K, V> {
* <p>
* Expired entries may be counted by {@link com.google.common.cache.Cache#size Cache.size()}, but will never be
* visible to read or write operations. Expired entries are currently cleaned up during write operations, or during
* occasional read operations in the absense of writes; though this behavior may change in the future.
* occasional read operations in the absence of writes; though this behavior may change in the future.
*
* @param duration the length of time after an entry is created that it should be automatically removed
* @param unit the unit that {@code duration} is expressed in
@@ -284,7 +284,7 @@ public class CompatibleCacheBuilder<K, V> {
*/
@SuppressWarnings("unchecked")
public <K1 extends K, V1 extends V> ConcurrentMap<K1, V1> build(CacheLoader<? super K1, V1> loader) {
Object cache = null;
Object cache;
if (BUILD_METHOD == null) {
try {

View File

@@ -13,6 +13,11 @@ public class SharedConfig {
@SuppressWarnings("unchecked")
public <T> T get(String path, T def) {
Object val = configValues.get(path);
if (def instanceof String) {
return (T) String.valueOf(val);
}
return ( val != null ) ? (T) val : def;
}

View File

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

View File

@@ -1,73 +0,0 @@
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(Connection source, DataSource target, AuthStorage storage) throws SQLException {
Statement stmt = null;
ResultSet resultSet = null;
try {
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");
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);
/* 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
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(resultSet);
}
}
}

View File

@@ -1,50 +0,0 @@
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;
public class BPAImporter extends Importer {
private static final String DEFAULT_TABLE_NAME = "users";
@Override
public int importData(Connection source, DataSource target, AuthStorage storage) throws SQLException {
Statement stmt = null;
ResultSet resultSet = null;
try {
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(resultSet);
}
}
}

View File

@@ -1,58 +0,0 @@
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;
public class ElDziAuthImporter extends Importer {
private static final String TABLE_NAME = "accounts";
@Override
public int importData(Connection source, DataSource target, AuthStorage storage) throws SQLException {
Statement stmt = null;
ResultSet resultSet = null;
try {
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(resultSet);
}
}
}

View File

@@ -1,20 +0,0 @@
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,21 +0,0 @@
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(Connection source, DataSource target, AuthStorage storage) throws SQLException;
protected void closeQuietly(AutoCloseable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception ignore) {
//ignore
}
}
}
}

View File

@@ -6,31 +6,34 @@ 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.Lists;
import com.google.common.collect.Sets;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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;
import java.util.stream.Collectors;
/**
* @param <P> Player class
* @param <C> CommandSender
* @param <T> Plugin class
*/
public abstract class FastLoginCore<P> {
public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
public static <K, V> ConcurrentMap<K, V> buildCache(int expireAfterWrite, int maxSize) {
CompatibleCacheBuilder<Object, Object> builder = CompatibleCacheBuilder.newBuilder();
@@ -54,31 +57,77 @@ public abstract class FastLoginCore<P> {
}
return UUID.fromString(withoutDashes.substring(0, 8)
+ "-" + withoutDashes.substring(8, 12)
+ "-" + withoutDashes.substring(12, 16)
+ "-" + withoutDashes.substring(16, 20)
+ "-" + withoutDashes.substring(20, 32));
+ '-' + 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 ConcurrentMap<String, Object> pendingLogin = FastLoginCore.buildCache(5, -1);
private final Set<UUID> pendingConfirms = Sets.newHashSet();
private final SharedConfig sharedConfig;
private final T plugin;
private 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 FastLoginCore(T plugin) {
this.plugin = plugin;
}
public void setApiConnector() {
public void load() {
saveDefaultFile("messages.yml");
saveDefaultFile("config.yml");
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("config.yml")));
sharedConfig = new SharedConfig(plugin.loadYamlFile(reader));
reader.close();
reader = Files.newBufferedReader(plugin.getDataFolder().toPath().resolve("config.yml"));
sharedConfig.getConfigValues().putAll(plugin.loadYamlFile(reader));
reader.close();
reader = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("messages.yml")));
reader = Files.newBufferedReader(plugin.getDataFolder().toPath().resolve("messages.yml"));
Map<String, Object> messageConfig = plugin.loadYamlFile(reader);
reader.close();
reader = Files.newBufferedReader(plugin.getDataFolder().toPath().resolve("messages.yml"));
messageConfig.putAll(plugin.loadYamlFile(reader));
for (Entry<String, Object> entry : messageConfig.entrySet()) {
String message = plugin.translateColorCodes('&', (String) entry.getValue());
if (!message.isEmpty()) {
localeMessages.put(entry.getKey(), message);
}
}
reader.close();
} catch (IOException ioEx) {
plugin.getLogger().log(Level.INFO, "Failed to load yaml files", ioEx);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, null, ex);
}
}
}
List<String> ipAddresses = sharedConfig.get("ip-addresses");
int requestLimit = sharedConfig.get("mojang-request-limit");
this.apiConnector = makeApiConnector(getLogger(), ipAddresses, requestLimit);
List<String> proxyList = sharedConfig.get("proxies", Lists.newArrayList());
Map<String, Integer> proxies = proxyList.stream()
.collect(Collectors
.toMap(line -> line.split(":")[0], line -> Integer.parseInt(line.split(":")[1])));
this.apiConnector = plugin.makeApiConnector(plugin.getLogger(), ipAddresses, requestLimit, proxies);
}
public MojangApiConnector getApiConnector() {
@@ -89,20 +138,21 @@ public abstract class FastLoginCore<P> {
return storage;
}
public abstract File getDataFolder();
public T getPlugin() {
return plugin;
}
public abstract Logger getLogger();
public abstract ThreadFactory getThreadFactory();
public void sendLocaleMessage(String key, C receiver) {
String message = localeMessages.get(key);
if (message != null) {
plugin.sendMessage(receiver, message);
}
}
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", "");
@@ -112,56 +162,18 @@ public abstract class FastLoginCore<P> {
String user = sharedConfig.get("username", "");
String password = sharedConfig.get("password", "");
storage = new AuthStorage(this, driver, host, port, database, user, password);
boolean useSSL = sharedConfig.get("useSSL", false);
storage = new AuthStorage(this, driver, host, port, database, user, password, useSSL);
try {
storage.createTables();
return true;
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "Failed to setup database. Disabling plugin...", ex);
plugin.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;
}
@@ -174,11 +186,11 @@ public abstract class FastLoginCore<P> {
this.passwordGenerator = passwordGenerator;
}
public ConcurrentMap<String, Object> getPendingLogins() {
return pendingLogins;
public ConcurrentMap<String, Object> getPendingLogin() {
return pendingLogin;
}
public Set<UUID> getPendingConfirms() {
public Collection<UUID> getPendingConfirms() {
return pendingConfirms;
}
@@ -190,6 +202,28 @@ public abstract class FastLoginCore<P> {
this.authPlugin = authPlugin;
}
public void saveDefaultFile(String fileName) {
if (!plugin.getDataFolder().exists()) {
plugin.getDataFolder().mkdir();
}
Path configFile = plugin.getDataFolder().toPath().resolve(fileName);
if (Files.notExists(configFile)) {
InputStream in = getClass().getClassLoader().getResourceAsStream(fileName);
try {
Files.copy(in, configFile);
} catch (IOException ioExc) {
plugin.getLogger().log(Level.SEVERE, "Error saving default " + fileName, ioExc);
} finally {
try {
in.close();
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, null, ex);
}
}
}
}
public void close() {
if (storage != null) {
storage.close();

View File

@@ -0,0 +1,106 @@
package com.github.games647.fastlogin.core.shared;
import com.github.games647.fastlogin.core.AuthStorage;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import java.util.logging.Level;
public abstract class ForceLoginManagement<P extends C, C, L extends LoginSession, T extends PlatformPlugin<C>>
implements Runnable {
protected final FastLoginCore<P, C, T> core;
protected final P player;
protected L session;
public ForceLoginManagement(FastLoginCore<P, C, T> core, P player) {
this.core = core;
this.player = player;
}
@Override
public void run() {
if (!isOnline(player) || session == null) {
return;
}
AuthStorage storage = core.getStorage();
PlayerProfile playerProfile = session.getProfile();
try {
if (isOnlineMode()) {
//premium player
AuthPlugin<P> authPlugin = core.getAuthPluginHook();
if (authPlugin == null) {
//maybe only bungeecord plugin
onForceActionSuccess(session);
} else {
boolean success = true;
String playerName = getName(player);
if (core.getConfig().get("autoLogin", true)) {
if (session.needsRegistration()
|| (core.getConfig().get("auto-register-unknown", false)
&& !authPlugin.isRegistered(playerName))) {
success = forceRegister(player);
} else {
success = forceLogin(player);
}
}
if (success) {
//update only on success to prevent corrupt data
if (playerProfile != null) {
playerProfile.setUuid(session.getUuid());
playerProfile.setPremium(true);
storage.save(playerProfile);
}
onForceActionSuccess(session);
}
}
} else if (playerProfile != null) {
//cracked player
playerProfile.setUuid(null);
playerProfile.setPremium(false);
storage.save(playerProfile);
}
} catch (Exception ex) {
core.getPlugin().getLogger().log(Level.WARNING, "ERROR ON FORCE LOGIN", ex);
}
}
public boolean forceRegister(P player) {
core.getPlugin().getLogger().log(Level.INFO, "Register player {0}", getName(player));
String generatedPassword = core.getPasswordGenerator().getRandomPassword(player);
boolean success = core.getAuthPluginHook().forceRegister(player, generatedPassword);
String message = core.getMessage("auto-register");
if (success && message != null) {
message = message.replace("%password", generatedPassword);
core.getPlugin().sendMessage(player, message);
}
return success;
}
public boolean forceLogin(P player) {
core.getPlugin().getLogger().log(Level.INFO, "Logging player {0} in", getName(player));
boolean success = core.getAuthPluginHook().forceLogin(player);
if (success) {
core.sendLocaleMessage("auto-login", player);
}
return success;
}
public abstract void onForceActionSuccess(LoginSession session);
public abstract String getName(P player);
public abstract boolean isOnline(P player);
public abstract boolean isOnlineMode();
}

View File

@@ -7,12 +7,12 @@ 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> {
public abstract class JoinManagement<P extends C, C, S extends LoginSource> {
protected final FastLoginCore<T> core;
protected final AuthPlugin<T> authHook;
protected final FastLoginCore<P, C, ?> core;
protected final AuthPlugin<P> authHook;
public JoinManagement(FastLoginCore<T> core, AuthPlugin<T> authHook) {
public JoinManagement(FastLoginCore<P, C, ?> core, AuthPlugin<P> authHook) {
this.core = core;
this.authHook = authHook;
}
@@ -26,10 +26,11 @@ public abstract class JoinManagement<T, S extends LoginSource> {
SharedConfig config = core.getConfig();
String ip = source.getAddress().getAddress().getHostAddress();
profile.setLastIp(ip);
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);
if (core.getPendingLogin().remove(ip + username) != null && config.get("secondAttemptCracked", false)) {
core.getPlugin().getLogger().log(Level.INFO, "Second attempt login -> cracked {0}", username);
//first login request failed so make a cracked session
startCrackedSession(source, profile, username);
@@ -38,7 +39,6 @@ public abstract class JoinManagement<T, S extends LoginSource> {
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);
}
@@ -56,15 +56,15 @@ public abstract class JoinManagement<T, S extends LoginSource> {
} 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);
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to check premium state", ex);
}
}
private boolean checkPremiumName(S source, String username, PlayerProfile profile) throws Exception {
core.getPlugin().getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
if (core.getConfig().get("autoRegister", false) && (authHook == null || !authHook.isRegistered(username))) {
requestPremiumLogin(source, profile, username, false);
return true;
@@ -79,7 +79,7 @@ public abstract class JoinManagement<T, S extends LoginSource> {
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);
core.getPlugin().getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
//update the username to the new one in the database
profile.setPlayerName(username);

View File

@@ -1,18 +1,25 @@
package com.github.games647.fastlogin.core.shared;
import com.github.games647.fastlogin.core.BalancedSSLFactory;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
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.InetSocketAddress;
import java.net.Proxy;
import java.net.Proxy.Type;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
@@ -28,8 +35,6 @@ public abstract class MojangApiConnector {
private static final int TIMEOUT = 3 * 1_000;
private static final String USER_AGENT = "Premium-Checker";
private static final String MCAPI_UUID_URL = "https://mcapi.ca/uuid/player/";
//only premium (paid account) users have a uuid from here
private static final String UUID_LINK = "https://api.mojang.com/users/profiles/minecraft/";
//this includes a-zA-Z1-9_
@@ -37,9 +42,10 @@ public abstract class MojangApiConnector {
private static final int RATE_LIMIT_CODE = 429;
//compile the pattern only on plugin enable -> and this have to be threadsafe
private final Pattern playernameMatcher = Pattern.compile(VALID_PLAYERNAME);
//compile the pattern only on plugin enable -> and this have to be thread-safe
private final Pattern nameMatcher = Pattern.compile(VALID_PLAYERNAME);
private final Iterator<Proxy> proxies;
private final ConcurrentMap<Object, Object> requests = FastLoginCore.buildCache(10, -1);
private final BalancedSSLFactory sslFactory;
private final int rateLimit;
@@ -47,17 +53,92 @@ public abstract class MojangApiConnector {
protected final Logger logger;
public MojangApiConnector(Logger logger, List<String> localAddresses, int rateLimit) {
public MojangApiConnector(Logger logger, Collection<String> localAddresses, int rateLimit
, Map<String, Integer> proxies) {
this.logger = logger;
if (rateLimit > 600) {
this.rateLimit = 600;
} else {
this.rateLimit = rateLimit;
this.rateLimit = Math.max(rateLimit, 600);
this.sslFactory = buildAddresses(logger, localAddresses);
List<Proxy> proxyBuilder = Lists.newArrayList();
for (Entry<String, Integer> proxy : proxies.entrySet()) {
proxyBuilder.add(new Proxy(Type.HTTP, new InetSocketAddress(proxy.getKey(), proxy.getValue())));
}
this.proxies = Iterables.cycle(proxyBuilder).iterator();
}
/**
* @return null on non-premium
*/
public UUID getPremiumUUID(String playerName) {
if (!nameMatcher.matcher(playerName).matches()) {
//check if it's a valid player name
return null;
}
try {
HttpsURLConnection connection;
if (requests.size() >= rateLimit || System.currentTimeMillis() - lastRateLimit < 1_000 * 60 * 10) {
synchronized (proxies) {
if (proxies.hasNext()) {
connection = getConnection(UUID_LINK + playerName, proxies.next());
} else {
return null;
}
}
} else {
requests.put(new Object(), new Object());
connection = getConnection(UUID_LINK + playerName);
}
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line = reader.readLine();
if (!"null".equals(line)) {
return FastLoginCore.parseId(getUUIDFromJson(line));
}
} else if (connection.getResponseCode() == RATE_LIMIT_CODE) {
logger.info("RATE_LIMIT REACHED");
lastRateLimit = System.currentTimeMillis();
if (!connection.usingProxy()) {
return getPremiumUUID(playerName);
}
}
//204 - no content for not found
} catch (Exception ex) {
logger.log(Level.SEVERE, "Failed to check if player has a paid account", ex);
}
return null;
}
public abstract boolean hasJoinedServer(LoginSession session, String serverId, InetSocketAddress ip);
protected abstract String getUUIDFromJson(String json);
protected HttpsURLConnection getConnection(String url, Proxy proxy) throws IOException {
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(proxy);
connection.setConnectTimeout(TIMEOUT);
connection.setReadTimeout(2 * TIMEOUT);
//the new Mojang API just uses json as response
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("User-Agent", USER_AGENT);
//this connection doesn't need to be closed. So can make use of keep alive in java
if (sslFactory != null) {
connection.setSSLSocketFactory(sslFactory);
}
return connection;
}
protected HttpsURLConnection getConnection(String url) throws IOException {
return getConnection(url, Proxy.NO_PROXY);
}
private BalancedSSLFactory buildAddresses(Logger logger, Collection<String> localAddresses) {
if (localAddresses.isEmpty()) {
this.sslFactory = null;
return null;
} else {
Set<InetAddress> addresses = Sets.newHashSet();
for (String localAddress : localAddresses) {
@@ -74,85 +155,7 @@ public abstract class MojangApiConnector {
}
}
this.sslFactory = new BalancedSSLFactory(HttpsURLConnection.getDefaultSSLSocketFactory(), addresses);
return new BalancedSSLFactory(HttpsURLConnection.getDefaultSSLSocketFactory(), addresses);
}
}
/**
*
* @param playerName
* @return null on non-premium
*/
public UUID getPremiumUUID(String playerName) {
//check if it's a valid playername
if (playernameMatcher.matcher(playerName).matches()) {
// only make a API call if the name is valid existing mojang account
if (requests.size() >= rateLimit || System.currentTimeMillis() - lastRateLimit < 1_000 * 60 * 10) {
// plugin.getLogger().fine("STILL WAITING FOR RATE_LIMIT - TRYING Third-party API");
return getUUIDFromAPI(playerName);
}
requests.put(new Object(), new Object());
try {
HttpsURLConnection connection = getConnection(UUID_LINK + playerName);
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line = reader.readLine();
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");
lastRateLimit = System.currentTimeMillis();
return getUUIDFromAPI(playerName);
}
//204 - no content for not found
} catch (Exception ex) {
logger.log(Level.SEVERE, "Failed to check if player has a paid account", ex);
}
//this connection doesn't need to be closed. So can make use of keep alive in java
}
return null;
}
public UUID getUUIDFromAPI(String playerName) {
try {
HttpURLConnection httpConnection = getConnection(MCAPI_UUID_URL + playerName);
if (httpConnection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
//cracked
return null;
}
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);
}
return null;
}
public abstract boolean hasJoinedServer(LoginSession session, String serverId);
protected abstract String getUUIDFromJson(String json);
protected HttpsURLConnection getConnection(String url) throws IOException {
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
connection.setConnectTimeout(TIMEOUT);
connection.setReadTimeout(2 * TIMEOUT);
//the new Mojang API just uses json as response
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("User-Agent", USER_AGENT);
if (sslFactory != null) {
connection.setSSLSocketFactory(sslFactory);
}
return connection;
}
}

View File

@@ -0,0 +1,28 @@
package com.github.games647.fastlogin.core.shared;
import java.io.File;
import java.io.Reader;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Logger;
public interface PlatformPlugin<C> {
String getName();
File getDataFolder();
Logger getLogger();
Map<String, Object> loadYamlFile(Reader reader);
void sendMessage(C receiver, String message);
ThreadFactory getThreadFactory();
String translateColorCodes(char colorChar, String rawMessage);
MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests
, Map<String, Integer> proxies);
}

View File

@@ -37,8 +37,8 @@ secondAttemptCracked: false
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, ...)
# uuid from that account. So if the player changes the username, they will still have
# the same player data (inventory, permissions, ...)
#
# Warning: This also means that the UUID will be different if the player is connecting
# through a offline mode connection. This **could** cause plugin compatibility issues.
@@ -49,14 +49,14 @@ switchMode: false
# players could still join the server, because they have different UUID.
#
# Moreover you may want to convert the offline UUID to a premium UUID. This will ensure that the player
# will have the same inventory, permissions, ... if they switched to premium authentification from offline/cracked
# authentification.
# will have the same inventory, permissions, ... if they switched to premium authentication from offline/cracked
# authentication.
#
# This feature requires Cauldron, Spigot or a fork of Spigot (PaperSpigot, TacoSpigot)
# This feature requires Cauldron, Spigot or a fork of Spigot (Paper)
premiumUuid: false
# This will make an additional check (only for player names which are not in the database) against the mojang servers
# in order to get the premium UUID. If that premium UUID is in the database, we can assume on sucessful login that the
# in order to get the premium UUID. If that premium UUID is in the database, we can assume on successful login that the
# player changed it's username and we just update the name in the database.
# Examples:
# #### Case 1
@@ -104,7 +104,7 @@ nameChangeCheck: false
#
# Keep in mind that this will only works if the player:
# * is the owner of the premium account
# * the serverconnection is established through a premium connection (paid account authentification)
# * the server connection is established through a premium connection (paid account authentication)
# * has a skin
#
# This means this plugin doesn't need to create a new connection to the Mojang servers, because
@@ -112,7 +112,7 @@ nameChangeCheck: false
# players like cracked player, you have to use other plugins.
#
# If you want to use skins for your cracked player, you need an additional plugin like
# ChangeSkin, SkinRestoer, ...
# ChangeSkin, SkinRestorer, ...
forwardSkin: true
# Displays a warning message that this message SHOULD only be invoked by
@@ -128,8 +128,8 @@ premium-warning: true
# Once the limit is reached, new players are always logged in as cracked until the rate-limit is expired.
# (to the next ten minutes)
#
# The limit is IP-wide. If you have multiple IPv4-Addreses you specify them here. FastLogin will then use it in rotating
# order --> 5 different IP-addreses 5 * 600 per 10 minutes
# The limit is IP-wide. If you have multiple IPv4-addresses you specify them here. FastLogin will then use it in rotating
# order --> 5 different IP-addresses 5 * 600 per 10 minutes
# If this list is empty only the default one will be used
#
# Lists are created like this:
@@ -137,13 +137,19 @@ premium-warning: true
# - 192-168-0-2
ip-addresses: []
# How many requests should be established until the plugin uses the third-party API https://mcapi.ca/
# Once this number is reached in a range of ten minutes it will start connecting to https://mcapi.ca/ for the next ten minutes
# This option exists in order to workaround the rate-limiting. Name -> UUID are fetched in the same way like heads
# How many requests should be established to the Mojang API for Name -> UUID requests. Some other plugins as well
# as the head minecraft block make such requests as well. Using this option you can limit the amount requests this
# plugin should make.
#
# If you want to join the discussion visit this: https://github.com/games647/FastLogin/issues/27#issuecomment-226954350
# If you lower this value, other plugins could still make requests while FastLogin cannot.
# Mojang limits the amount of request to 600 per 10 minutes per IPv4-address.
mojang-request-limit: 600
# This option automatically registers players which are in the FastLogin database, but not in the auth plugin database.
# This can happen if you switch your auth plugin or cleared the database of the auth plugin.
# https://github.com/games647/FastLogin/issues/85
auto-register-unknown: false
# This disables the auto login from fastlogin. So a premium (like a paid account) authentication is requested, but
# the player won't be auto logged into the account.
#
@@ -152,17 +158,30 @@ mojang-request-limit: 600
autoLogin: true
# Database configuration
# Recommened is the use of MariaDB (a better version of MySQL)
# Recommended is the use of MariaDB (a better version of MySQL)
# Single file SQLite database
driver: org.sqlite.JDBC
# File location
database: '{pluginDir}/FastLogin.db'
# MySQL
# MySQL/MariaDB
#driver: com.mysql.jdbc.Driver
#host: localhost
#port: 3306
#database: fastlogin
#username: myUser
#password: myPassword
#password: myPassword
# It's strongly recommended to enable SSL and setup a SSL certificate if the MySQL server isn't running on the same
# machine
#useSSL: false
# HTTP proxies for connecting to the Mojang servers in order to check if the username of a player is premium.
# This is a workaround to prevent rate-limiting by Mojang. These proxies will only be used once your server hit
# the rate-limit or the custom value above.
# Please make sure you use reliable proxies.
proxies:
# 'IP:Port' or 'Domain:Port'
# - 'xyz.com:1337'
# - 'test.com:5131'

View File

@@ -8,7 +8,7 @@
# You want to have language template? Visit the Github Wiki here:
# https://github.com/games647/FastLogin/wiki/English
# In order to split a message into seperate lines you could just make a new line, but keep the '
# In order to split a message into separate lines you could just make a new line, but keep the '
# Example:
# bla: '&aFirst line
# Second line
@@ -24,10 +24,10 @@
# 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
# Player activated premium login 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
# Player activated premium login 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
@@ -48,7 +48,7 @@ not-premium-other: '&4Player is not in the premium list'
# Admin wanted to change the premium of a user that isn't known to the plugin
player-unknown: '&4Player not in the database'
# ========= Bukkit/Spigot/PaperSpigot/TacoSpigot only ================================
# ========= Bukkit/Spigot ================
# The user skipped the authentication, because it was a premium player
auto-login: '&2Auto logged in'
@@ -73,17 +73,17 @@ wait-on-proxy: '&6Sending request...'
# authentication. In this state the client expects a success packet with a encrypted connection or disconnect packet.
# So we kick the player, if we cannot encrypt the connection. In other situation (example: premium name check),
# the player will be just authenticated as cracked
error-kick: '&4Error occured'
error-kick: '&4Error occurred'
# The server sents a verify token within the premium authentication reqest. If this doesn't match on response,
# The server sends a verify token within the premium authentication request. If this doesn't match on response,
# it could be another client sending malicious packets
invalid-verify-token: '&4Invalid token'
# The client sent no request join server request to the mojang servers which would proof that it's owner of that
# acciunt. Only modified clients would do this.
# account. Only modified clients would do this.
invalid-session: '&4Invalid session'
# The client sent a malicous packet without a login request packet
# The client sent a malicious packet without a login request packet
invalid-requst: '&4Invalid request'
# Message if the bukkit isn't fully started to inject the packets

44
pom.xml
View File

@@ -8,7 +8,7 @@
<packaging>pom</packaging>
<name>FastLogin</name>
<version>1.9</version>
<version>1.10</version>
<inceptionYear>2015</inceptionYear>
<url>https://www.spigotmc.org/resources/fastlogin.14153/</url>
<description>
@@ -19,6 +19,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--Possibility to deploy directly to the plugins folder-->
<outputDir>${basedir}/target</outputDir>
<git.commit.id>Unknown</git.commit.id>
</properties>
<modules>
@@ -28,17 +29,6 @@
<module>universal</module>
</modules>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/games647/FastLogin/issues</url>
</issueManagement>
<scm>
<url>https://github.com/games647/FastLogin</url>
<connection>scm:git:git://github.com/games647/FastLogin.git</connection>
<developerConnection>scm:git:ssh://git@github.com:games647/FastLogin.git</developerConnection>
</scm>
<build>
<defaultGoal>install</defaultGoal>
<!--Just use the project name to replace an old version of the plugin if the user does only copy-paste-->
@@ -48,14 +38,29 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<version>2.2.2</version>
<configuration>
<failOnNoGitDirectory>false</failOnNoGitDirectory>
</configuration>
<executions>
<execution>
<id>get-the-git-infos</id>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
@@ -64,15 +69,6 @@
<!--Replace variables-->
<filtering>true</filtering>
</resource>
<!--Add the license to jar in order to see it in the final jar-->
<resource>
<!--Parent folder-->
<directory>${basedir}/..</directory>
<includes>
<include>LICENSE</include>
</includes>
</resource>
</resources>
</build>
</project>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.9</version>
<version>1.10</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -22,7 +22,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<version>3.0.0</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>false</shadedArtifactAttached>
@@ -43,15 +43,6 @@
</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>