Merge pull request #776 from AuthMe/master

PRE-BETA 4 STUFF
This commit is contained in:
Gabriele C 2016-08-22 20:19:10 +02:00 committed by GitHub
commit afd4498184
299 changed files with 10049 additions and 6258 deletions

25
.github/ISSUE_TEMPLATE.MD vendored Normal file
View File

@ -0,0 +1,25 @@
####Before reporting an issue make sure you are running the latest build of the plugin!
### What behaviour is observed:
What happened?
### What behaviour is expected:
What did you expect?
### Steps/models to reproduce:
The actions that cause the issue
### Plugin list:
This can be found by running `/pl`
### Environment description
Standalone server/Bungeecord network, SQLite/MYSql, ...
### AuthMe build number:
This can be found by running `/authme version`
### Error Log:
Pastebin/Hastebin/Gist link of the error logo or stacktrace (if any)
### Configuration:
Pastebin/Hastebin/Gist link of your config.yml file (remember to delete any sensible data)

View File

@ -12,9 +12,280 @@
<XML> <XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" /> <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML> </XML>
<codeStyleSettings language="JAVA">
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<FINAL>true</FINAL>
<PUBLIC>true</PUBLIC>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<FINAL>true</FINAL>
<PROTECTED>true</PROTECTED>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<FINAL>true</FINAL>
<PACKAGE_PRIVATE>true</PACKAGE_PRIVATE>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<FINAL>true</FINAL>
<PRIVATE>true</PRIVATE>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<PUBLIC>true</PUBLIC>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<PROTECTED>true</PROTECTED>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<PACKAGE_PRIVATE>true</PACKAGE_PRIVATE>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<PRIVATE>true</PRIVATE>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<INITIALIZER_BLOCK>true</INITIALIZER_BLOCK>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<FINAL>true</FINAL>
<PUBLIC>true</PUBLIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<FINAL>true</FINAL>
<PROTECTED>true</PROTECTED>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<FINAL>true</FINAL>
<PACKAGE_PRIVATE>true</PACKAGE_PRIVATE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<FINAL>true</FINAL>
<PRIVATE>true</PRIVATE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<PUBLIC>true</PUBLIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<PROTECTED>true</PROTECTED>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<PACKAGE_PRIVATE>true</PACKAGE_PRIVATE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD>true</FIELD>
<PRIVATE>true</PRIVATE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<FIELD>true</FIELD>
</match>
</rule>
</section>
<section>
<rule>
<match>
<INITIALIZER_BLOCK>true</INITIALIZER_BLOCK>
</match>
</rule>
</section>
<section>
<rule>
<match>
<CONSTRUCTOR>true</CONSTRUCTOR>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD>true</METHOD>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<METHOD>true</METHOD>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD>true</METHOD>
<PRIVATE>true</PRIVATE>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<ENUM>true</ENUM>
</match>
</rule>
</section>
<section>
<rule>
<match>
<INTERFACE>true</INTERFACE>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<CLASS>true</CLASS>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<CLASS>true</CLASS>
</match>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</value> </value>
</option> </option>
<option name="USE_PER_PROJECT_SETTINGS" value="true" /> <option name="USE_PER_PROJECT_SETTINGS" value="true" />
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Project" />
</component> </component>
</project> </project>

View File

@ -2,23 +2,23 @@
<p align="center"><strong>The most used authentication plugin for CraftBukkit/Spigot!</strong></p> <p align="center"><strong>The most used authentication plugin for CraftBukkit/Spigot!</strong></p>
<hr> <hr>
#####News:
- Latest version of AuthMeBridge is finally compatible with latest AuthMe snapshots! ;)
#####Development tools: #####Development tools:
- DEVELOPMENT TEAM REPO (<strong>please send PRs here!</strong>): <a href="https://github.com/AuthMe-Team/AuthMeReloaded">Github Development Page</a> - MAIN REPO (**release sources, issue tracker!**): [Github Main Page](https://github.com/Xephi/AuthMeReloaded)
- DEVELOPMENT TEAM REPO (**latest sources, please send PRs here!**): [Github Development Page](https://github.com/AuthMe/AuthMeReloaded)
- Developers ChatRoom: [![Join the chat at https://gitter.im/Xephi/AuthMeReloaded](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Xephi/AuthMeReloaded?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - Developers ChatRoom: [![Join the chat at https://gitter.im/Xephi/AuthMeReloaded](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Xephi/AuthMeReloaded?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
- Build Server (<strong>DEVELOPMENT BUILDS</strong>): <a href="http://ci.xephi.fr/job/AuthMeReloaded">Xephi's Jenkins</a> - Build Server (**DEVELOPMENT BUILDS**): [Xephi's Jenkins](http://ci.xephi.fr/job/AuthMeReloaded)
- Build status: [![Build Status](https://travis-ci.org/AuthMe-Team/AuthMeReloaded.svg?branch=master)](https://travis-ci.org/AuthMe-Team/AuthMeReloaded) [![Dependency Status](https://www.versioneye.com/user/projects/55bab9e8653762002000190a/badge.svg?style=flat)](https://www.versioneye.com/user/projects/55bab9e8653762002000190a) - Build status: [![Build Status](https://travis-ci.org/AuthMe/AuthMeReloaded.svg?branch=master)](https://travis-ci.org/AuthMe/AuthMeReloaded)
- Build status (CircleCI): [![Circle CI](https://circleci.com/gh/AuthMe-Team/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe-Team/AuthMeReloaded) - Dependencies: [![Dependency Status](https://www.versioneye.com/user/projects/5617ca36a193340f28000222/badge.svg)](https://www.versioneye.com/user/projects/5617ca36a193340f28000222)
- Alternative Dev Build download link (via CircleCi): <a href="https://circleci-tkn.rhcloud.com/api/v1/project/AuthMe-Team/AuthMeReloaded/tree/master/latest/artifacts/AuthMe.jar">Download</a>
- JitPack (just in case): [![](https://jitpack.io/v/AuthMe-Team/AuthMeReloaded.svg)](https://jitpack.io/#AuthMe-Team/AuthMeReloaded) - Build status (CircleCI): [![Circle CI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded)
- Alternative Dev Build download link (via CircleCi): <a href="https://circleci-tkn.rhcloud.com/api/v1/project/AuthMe/AuthMeReloaded/tree/master/latest/artifacts/AuthMe.jar">Download</a>
- JitPack (just in case): [![](https://jitpack.io/v/AuthMe/AuthMeReloaded.svg)](https://jitpack.io/#AuthMe/AuthMeReloaded)
- Code Coverage: [![Coverage Status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master) - Code Coverage: [![Coverage Status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master)
@ -39,6 +39,7 @@ McStats: http://mcstats.org/plugin/AuthMe
<img src="http://i.mcstats.org/AuthMe/Version+Demographics.borderless.png"> <img src="http://i.mcstats.org/AuthMe/Version+Demographics.borderless.png">
#####Development history: #####Development history:
Outdated!
[![Gource AuthMe History Video](http://img.youtube.com/vi/hJRzNfYyd9k/hqdefault.jpg)](https://www.youtube.com/watch?v=hJRzNfYyd9k) [![Gource AuthMe History Video](http://img.youtube.com/vi/hJRzNfYyd9k/hqdefault.jpg)](https://www.youtube.com/watch?v=hJRzNfYyd9k)
<hr> <hr>
@ -53,8 +54,8 @@ McStats: http://mcstats.org/plugin/AuthMe
>- Execute command "mvn clean install" >- Execute command "mvn clean install"
#####Running Requirements: #####Running Requirements:
>- Java 1.7 (should work also with Java 1.8) >- Java 1.7 or 1.8
>- PaperSpigot, Spigot or CraftBukkit (1.7.10, 1.8.X or 1.9.X) >- PaperSpigot, Spigot or CraftBukkit (1.7.10, 1.8.X, 1.9.X, 1.10.X)
>- ProtocolLib (optional, required by the protectInventory feature) >- ProtocolLib (optional, required by the protectInventory feature)
<hr> <hr>
@ -62,13 +63,16 @@ McStats: http://mcstats.org/plugin/AuthMe
#####"The best authentication plugin for the Bukkit/Spigot API!" #####"The best authentication plugin for the Bukkit/Spigot API!"
<p>Prevent username stealing on your server!<br> Prevent username stealing on your server!<br>
Use it to secure your Offline mode server or to increase your Online mode server's protection!</p> Use it to secure your Offline mode server or to increase your Online mode server's protection!
<p>AuthMeReloaded disallows players who aren't authenticated to do actions like placing blocks, moving,<br> AuthMeReloaded disallows players who aren't authenticated to do actions like placing blocks, moving,<br>
typing commands or using the inventory. It can also kick players with uncommonly long or short player names or kick players from banned countries.</p> typing commands or using the inventory. It can also kick players with uncommonly long or short player names or kick players from banned countries.
<p>With the Session Login feature you don't have to execute the authentication command every time you connect to the server! Each command and every feature can be enabled or disabled from our well structured configuration file.</p>
<p>You can also create your own translation file and, if you want, you can share it with us! :)</p> With the Session Login feature you don't have to execute the authentication command every time you connect to the server!
Each command and every feature can be enabled or disabled from our well structured configuration file.
You can also create your own translation file and, if you want, you can share it with us! :)
####Features: ####Features:
<ul> <ul>
@ -114,24 +118,25 @@ typing commands or using the inventory. It can also kick players with uncommonly
####Email Recovery Dependency ####Email Recovery Dependency
<a href="http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-configure-email-recovery-system/">How to configure email recovery system?</a> <a href="http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-configure-email-recovery-system/">How to configure email recovery system?</a>
####Commands ####Commands
<a href="https://github.com/AuthMe-Team/AuthMeReloaded/blob/master/docs/commands.md">Command list and usage</a> [Command list and usage](https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/commands.md)
####Permissions ####Permissions
<ul><li>authme.player.* - for all user commands - authme.player.* - for all user commands
</li><li>authme.admin.* - for all admin commands - authme.admin.* - for all admin commands
</li><li><a href="https://github.com/AuthMe-Team/AuthMeReloaded/blob/master/docs/permission_nodes.md">List of all single permissions</a> - [List of all permission nodes](http://github.com/AuthMe-Team/AuthMeReloaded/blob/master/docs/permission_nodes.md)
</li></ul>
####How To ####How To
<ul><li><a href="http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-install-and-initial-configuration/">How to Install and Setup</a> - [How to install and set up](http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-install-and-initial-configuration/)
</li><li><a href="http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-import-database-from-xauth/">How to import database from xAuth</a> - [How to import database from xAuth](http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-import-database-from-xauth/)
</li><li><a href="http://dev.bukkit.org/server-mods/authme-reloaded/pages/web-site-integration/">Website Integration</a> - [Website integration](http://dev.bukkit.org/server-mods/authme-reloaded/pages/web-site-integration/)
</li><li><a href="https://raw.githubusercontent.com/Xephi/AuthMeReloaded/master/src/main/resources/config.yml">Click here for an example of the Config file</a> - [Click here for an example of the config file](https://raw.githubusercontent.com/Xephi/AuthMeReloaded/master/src/main/resources/config.yml)
</li><li><a href="http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-import-database-from-rakamak/">How to convert from Rakamak</a> - [How to convert from Rakamak](http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-import-database-from-rakamak/)
</li><li>Convert from FlatFile (auths.db but not the sqlite one) to MySQL: /authme converter - Convert from FlatFile (auths.db but not the sqlite one) to MySQL: /authme converter
</li></ul>
<hr> <hr>
#####GeoIP #####GeoIP
<p>This product uses data from the GeoLite API created by MaxMind, available at <a href="http://www.maxmind.com">http://www.maxmind.com</a></p> This product uses data from the GeoLite API created by MaxMind, available at http://www.maxmind.com
<hr> <hr>
#####Donate #####Donate
@ -144,6 +149,6 @@ GameHosting.it is leader in Italy as Game Server Provider. With its own DataCent
[![GameHosting](http://www.gamehosting.it/images/bn3.png)](http://www.gamehosting.it) [![GameHosting](http://www.gamehosting.it/images/bn3.png)](http://www.gamehosting.it)
#####Credits #####Credits
<p>Team members: look at the <a href="https://github.com/AuthMe-Team/AuthMeReloaded/blob/master/team.txt">team.txt file</a> <p>Team members: look at the <a href="https://github.com/AuthMe/AuthMeReloaded/blob/master/team.txt">team.txt file</a>
<p>Credit for old version of the plugin to: d4rkwarriors, fabe1337, Whoami2 and pomo4ka</p> <p>Credit for old version of the plugin to: d4rkwarriors, fabe1337, Whoami2 and pomo4ka</p>
<p>Thanks also to: AS1LV3RN1NJA, Hoeze and eprimex</p> <p>Thanks also to: AS1LV3RN1NJA, Hoeze and eprimex</p>

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly --> <!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Thu Apr 07 17:17:20 CEST 2016. See commands/commands.tpl.md --> <!-- File auto-generated on Wed Jun 22 17:39:14 EDT 2016. See commands/commands.tpl.md -->
## AuthMe Commands ## AuthMe Commands
You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >` You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >`
@ -55,9 +55,9 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
- **/register** [password] [verifyPassword]: Command to register using AuthMeReloaded. - **/register** [password] [verifyPassword]: Command to register using AuthMeReloaded.
<br />Requires `authme.player.register` <br />Requires `authme.player.register`
- **/register help** [query]: View detailed help for /register commands. - **/register help** [query]: View detailed help for /register commands.
- **/unreg** &lt;password>: Command to unregister using AuthMeReloaded. - **/unregister** &lt;password>: Command to unregister using AuthMeReloaded.
<br />Requires `authme.player.unregister` <br />Requires `authme.player.unregister`
- **/unreg help** [query]: View detailed help for /unreg commands. - **/unregister help** [query]: View detailed help for /unregister commands.
- **/changepassword** &lt;oldPassword> &lt;newPassword>: Command to change your password using AuthMeReloaded. - **/changepassword** &lt;oldPassword> &lt;newPassword>: Command to change your password using AuthMeReloaded.
<br />Requires `authme.player.changepassword` <br />Requires `authme.player.changepassword`
- **/changepassword help** [query]: View detailed help for /changepassword commands. - **/changepassword help** [query]: View detailed help for /changepassword commands.
@ -73,6 +73,7 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
<br />Requires `authme.player.captcha` <br />Requires `authme.player.captcha`
- **/captcha help** [query]: View detailed help for /captcha commands. - **/captcha help** [query]: View detailed help for /captcha commands.
--- ---
This page was automatically generated on the [AuthMe-Team/AuthMeReloaded repository](https://github.com/AuthMe-Team/AuthMeReloaded/tree/master/docs/) on Thu Apr 07 17:17:20 CEST 2016 This page was automatically generated on the [AuthMe-Team/AuthMeReloaded repository](https://github.com/AuthMe-Team/AuthMeReloaded/tree/master/docs/) on Wed Jun 22 17:39:14 EDT 2016

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly --> <!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Thu Apr 07 17:17:22 CEST 2016. See hashmethods/hash_algorithms.tpl.md --> <!-- File auto-generated on Wed Jun 22 17:39:16 EDT 2016. See hashmethods/hash_algorithms.tpl.md -->
## Hash Algorithms ## Hash Algorithms
AuthMe supports the following hash algorithms for storing your passwords safely. AuthMe supports the following hash algorithms for storing your passwords safely.
@ -82,4 +82,4 @@ or bad.
--- ---
This page was automatically generated on the [AuthMe-Team/AuthMeReloaded repository](https://github.com/AuthMe-Team/AuthMeReloaded/tree/master/docs/) on Thu Apr 07 17:17:22 CEST 2016 This page was automatically generated on the [AuthMe-Team/AuthMeReloaded repository](https://github.com/AuthMe-Team/AuthMeReloaded/tree/master/docs/) on Wed Jun 22 17:39:16 EDT 2016

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly --> <!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Thu Apr 07 17:17:24 CEST 2016. See permissions/permission_nodes.tpl.md --> <!-- File auto-generated on Wed Jun 22 17:39:29 EDT 2016. See permissions/permission_nodes.tpl.md -->
## AuthMe Permission Nodes ## AuthMe Permission Nodes
The following are the permission nodes that are currently supported by the latest dev builds. The following are the permission nodes that are currently supported by the latest dev builds.
@ -28,6 +28,7 @@ The following are the permission nodes that are currently supported by the lates
- **authme.allowmultipleaccounts** Permission to be able to register multiple accounts. - **authme.allowmultipleaccounts** Permission to be able to register multiple accounts.
- **authme.bypassantibot** Permission node to bypass AntiBot protection. - **authme.bypassantibot** Permission node to bypass AntiBot protection.
- **authme.bypassforcesurvival** Permission for users to bypass force-survival mode. - **authme.bypassforcesurvival** Permission for users to bypass force-survival mode.
- **authme.bypasspurge** Permission to bypass the purging process
- **authme.player.*** Permission to use all player (non-admin) commands. - **authme.player.*** Permission to use all player (non-admin) commands.
- **authme.player.canbeforced** Permission for users a login can be forced to. - **authme.player.canbeforced** Permission for users a login can be forced to.
- **authme.player.captcha** Command permission to use captcha. - **authme.player.captcha** Command permission to use captcha.
@ -42,6 +43,7 @@ The following are the permission nodes that are currently supported by the lates
- **authme.player.unregister** Command permission to unregister. - **authme.player.unregister** Command permission to unregister.
- **authme.vip** Permission node to identify VIP users. - **authme.vip** Permission node to identify VIP users.
--- ---
This page was automatically generated on the [AuthMe-Team/AuthMeReloaded repository](https://github.com/AuthMe-Team/AuthMeReloaded/tree/master/docs/) on Thu Apr 07 17:17:24 CEST 2016 This page was automatically generated on the [AuthMe-Team/AuthMeReloaded repository](https://github.com/AuthMe-Team/AuthMeReloaded/tree/master/docs/) on Wed Jun 22 17:39:29 EDT 2016

260
pom.xml
View File

@ -6,8 +6,7 @@
<groupId>fr.xephi</groupId> <groupId>fr.xephi</groupId>
<artifactId>authme</artifactId> <artifactId>authme</artifactId>
<version>5.2-BETA3</version> <version>5.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>AuthMeReloaded</name> <name>AuthMeReloaded</name>
<description>The first authentication plugin for the Bukkit API!</description> <description>The first authentication plugin for the Bukkit API!</description>
@ -17,13 +16,13 @@
<organization> <organization>
<name>AuthMe-Team</name> <name>AuthMe-Team</name>
<url>https://github.com/AuthMe-Team</url> <url>https://github.com/AuthMe</url>
</organization> </organization>
<scm> <scm>
<connection>scm:git:https://github.com/AuthMe-Team/AuthMeReloaded.git</connection> <connection>scm:git:https://github.com/AuthMe/AuthMeReloaded.git</connection>
<developerConnection>scm:git:git@github.com:AuthMe-Team/AuthMeReloaded.git</developerConnection> <developerConnection>scm:git:git@github.com:AuthMe/AuthMeReloaded.git</developerConnection>
<url>https://github.com/AuthMe-Team/AuthMeReloaded</url> <url>https://github.com/AuthMe/AuthMeReloaded</url>
</scm> </scm>
<ciManagement> <ciManagement>
@ -44,10 +43,6 @@
</license> </license>
</licenses> </licenses>
<prerequisites>
<maven>3.3.3</maven>
</prerequisites>
<properties> <properties>
<!-- Project properties --> <!-- Project properties -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@ -62,15 +57,17 @@
<!-- BukkitPlugin properties --> <!-- BukkitPlugin properties -->
<bukkitplugin.name>${project.outputName}</bukkitplugin.name> <bukkitplugin.name>${project.outputName}</bukkitplugin.name>
<bukkitplugin.version>${project.versionCode}</bukkitplugin.version>
<bukkitplugin.main>${project.groupId}.${project.artifactId}.${bukkitplugin.name}</bukkitplugin.main> <bukkitplugin.main>${project.groupId}.${project.artifactId}.${bukkitplugin.name}</bukkitplugin.main>
<bukkitplugin.authors>Xephi, sgdc3, DNx5, timvisee, games647, ljacqu</bukkitplugin.authors> <bukkitplugin.authors>Xephi, sgdc3, DNx5, timvisee, games647, ljacqu, Gnat008</bukkitplugin.authors>
<!-- Change Bukkit Version HERE! --> <!-- Change Bukkit Version HERE! -->
<bukkit.version>1.10-R0.1-SNAPSHOT</bukkit.version> <bukkit.version>1.10-R0.1-SNAPSHOT</bukkit.version>
</properties> </properties>
<!-- Jenkins profile (add the real buildNumber to the version string) --> <!-- Jenkins profile -->
<profiles> <profiles>
<!-- Set the buildNumber using the jenkins env. variable -->
<profile> <profile>
<id>jenkins</id> <id>jenkins</id>
<activation> <activation>
@ -82,6 +79,7 @@
<project.buildNumber>${env.BUILD_NUMBER}</project.buildNumber> <project.buildNumber>${env.BUILD_NUMBER}</project.buildNumber>
</properties> </properties>
</profile> </profile>
<!-- Skip long hash tests, reduce the test time of 20-30 seconds -->
<profile> <profile>
<id>skipLongHashTests</id> <id>skipLongHashTests</id>
<activation> <activation>
@ -93,10 +91,85 @@
<project.skipExtendedHashTests>true</project.skipExtendedHashTests> <project.skipExtendedHashTests>true</project.skipExtendedHashTests>
</properties> </properties>
</profile> </profile>
<!-- Spigot, default -->
<profile>
<id>spigot</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${bukkit.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>junit</artifactId>
<groupId>junit</groupId>
</exclusion>
<exclusion>
<artifactId>json-simple</artifactId>
<groupId>com.googlecode.json-simple</groupId>
</exclusion>
<exclusion>
<artifactId>persistence-api</artifactId>
<groupId>javax.persistence</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
<exclusion>
<artifactId>bungeecord-chat</artifactId>
<groupId>net.md-5</groupId>
</exclusion>
<exclusion>
<artifactId>gson</artifactId>
<groupId>com.google.code.gson</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</profile>
<!-- Bukkit -->
<profile>
<id>bukkit</id>
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
<version>${bukkit.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>junit</artifactId>
<groupId>junit</groupId>
</exclusion>
<exclusion>
<artifactId>json-simple</artifactId>
<groupId>com.googlecode.json-simple</groupId>
</exclusion>
<exclusion>
<artifactId>persistence-api</artifactId>
<groupId>javax.persistence</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
<exclusion>
<artifactId>gson</artifactId>
<groupId>com.google.code.gson</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</profile>
</profiles> </profiles>
<build> <build>
<!-- Name of the NOSHADE jar (no shaded/relocated libraries) --> <!-- Name of the unshaded jar (no shaded/relocated libraries) -->
<finalName>${project.finalName}-noshade</finalName> <finalName>${project.finalName}-noshade</finalName>
<resources> <resources>
@ -135,6 +208,7 @@
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version> <version>2.19.1</version>
<configuration> <configuration>
<!-- Force the right file encoding during unit testing -->
<argLine>-Dfile.encoding=${project.build.sourceEncoding} @{argLine}</argLine> <argLine>-Dfile.encoding=${project.build.sourceEncoding} @{argLine}</argLine>
<systemPropertyVariables> <systemPropertyVariables>
<project.skipExtendedHashTests>${project.skipExtendedHashTests}</project.skipExtendedHashTests> <project.skipExtendedHashTests>${project.skipExtendedHashTests}</project.skipExtendedHashTests>
@ -143,20 +217,16 @@
</plugin> </plugin>
<!-- Libs Shading and Relocation --> <!-- Libs Shading and Relocation -->
<plugin> <plugin>
<!--
Relocate all lib we use in order to fix class loading errors if we use different versions
than already loaded libs (i.e. by Mojang -> gson)
-->
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version> <version>2.4.3</version>
<configuration> <configuration>
<createDependencyReducedPom>false</createDependencyReducedPom> <createDependencyReducedPom>false</createDependencyReducedPom>
<!--
Let's try to remove this!
<minimizeJar>false</minimizeJar>
-->
</configuration> </configuration>
<!--
Relocate all lib we use in order to fix class loading errors if we use different versions
than already loaded libs (i.e. by Mojang -> gson)
-->
<executions> <executions>
<!-- Spigot 1.8+ --> <!-- Spigot 1.8+ -->
<execution> <execution>
@ -170,39 +240,35 @@
<artifactSet> <artifactSet>
<excludes> <excludes>
<exclude>com.google.guava:guava</exclude> <exclude>com.google.guava:guava</exclude>
<exclude>com.google.code.gson:gson</exclude>
</excludes> </excludes>
</artifactSet> </artifactSet>
<relocations> <relocations>
<!-- We use a newer version of gson so we need to include it! --> <relocation>
<relocation> <pattern>com.zaxxer.hikari</pattern>
<pattern>com.google.gson</pattern> <shadedPattern>fr.xephi.authme.libs.zaxxer.hikari</shadedPattern>
<shadedPattern>fr.xephi.authme.libs.google</shadedPattern> </relocation>
</relocation> <relocation>
<relocation> <pattern>org.slf4j</pattern>
<pattern>com.zaxxer.hikari</pattern> <shadedPattern>fr.xephi.authme.libs.slf4j.slf4j</shadedPattern>
<shadedPattern>fr.xephi.authme.libs.hikari</shadedPattern> </relocation>
</relocation> <relocation>
<relocation> <pattern>com.maxmind.geoip</pattern>
<pattern>org.slf4j</pattern> <shadedPattern>fr.xephi.authme.libs.maxmind.geoip</shadedPattern>
<shadedPattern>fr.xephi.authme.libs.slf4j</shadedPattern> </relocation>
</relocation> <relocation>
<relocation> <pattern>net.ricecode.similarity</pattern>
<pattern>com.maxmind.geoip</pattern> <shadedPattern>fr.xephi.authme.libs.ricecode.similarity</shadedPattern>
<shadedPattern>fr.xephi.authme.libs.geoip</shadedPattern> </relocation>
</relocation> <relocation>
<relocation> <pattern>javax.inject</pattern>
<pattern>net.ricecode.similarity</pattern> <shadedPattern>fr.xephi.authme.libs.javax.inject</shadedPattern>
<shadedPattern>fr.xephi.authme.libs.similarity</shadedPattern> </relocation>
</relocation> <!-- MCStats.org metrics -->
<relocation> <relocation>
<pattern>javax.inject</pattern> <pattern>org.mcstats</pattern>
<shadedPattern>fr.xephi.authme.libs.inject</shadedPattern> <shadedPattern>fr.xephi.authme</shadedPattern>
</relocation> </relocation>
<!-- MCStats.org metrics -->
<relocation>
<pattern>org.mcstats</pattern>
<shadedPattern>fr.xephi.authme</shadedPattern>
</relocation>
</relocations> </relocations>
<outputFile>target/${project.finalName}-spigot.jar</outputFile> <outputFile>target/${project.finalName}-spigot.jar</outputFile>
</configuration> </configuration>
@ -215,31 +281,31 @@
<goal>shade</goal> <goal>shade</goal>
</goals> </goals>
<configuration> <configuration>
<!-- Include all google libraries, because they are not available before 1.8 -->
<relocations> <relocations>
<!-- Include all google libraries, because they are not available before 1.8 -->
<relocation> <relocation>
<pattern>com.google</pattern> <pattern>com.google</pattern>
<shadedPattern>fr.xephi.authme.libs.google</shadedPattern> <shadedPattern>fr.xephi.authme.libs.google</shadedPattern>
</relocation> </relocation>
<relocation> <relocation>
<pattern>com.zaxxer.hikari</pattern> <pattern>com.zaxxer.hikari</pattern>
<shadedPattern>fr.xephi.authme.libs.hikari</shadedPattern> <shadedPattern>fr.xephi.authme.libs.zaxxer.hikari</shadedPattern>
</relocation> </relocation>
<relocation> <relocation>
<pattern>org.slf4j</pattern> <pattern>org.slf4j</pattern>
<shadedPattern>fr.xephi.authme.libs.slf4j</shadedPattern> <shadedPattern>fr.xephi.authme.libs.slf4j.slf4j</shadedPattern>
</relocation> </relocation>
<relocation> <relocation>
<pattern>com.maxmind.geoip</pattern> <pattern>com.maxmind.geoip</pattern>
<shadedPattern>fr.xephi.authme.libs.geoip</shadedPattern> <shadedPattern>fr.xephi.authme.libs.maxmind.geoip</shadedPattern>
</relocation> </relocation>
<relocation> <relocation>
<pattern>net.ricecode.similarity</pattern> <pattern>net.ricecode.similarity</pattern>
<shadedPattern>fr.xephi.authme.libs.similarity</shadedPattern> <shadedPattern>fr.xephi.authme.libs.ricecode.similarity</shadedPattern>
</relocation> </relocation>
<relocation> <relocation>
<pattern>javax.inject</pattern> <pattern>javax.inject</pattern>
<shadedPattern>fr.xephi.authme.libs.inject</shadedPattern> <shadedPattern>fr.xephi.authme.libs.javax.inject</shadedPattern>
</relocation> </relocation>
<!-- MCStats.org metrics --> <!-- MCStats.org metrics -->
<relocation> <relocation>
@ -252,7 +318,7 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<!-- Exec Tools --> <!-- Exec Plugin (Tools runner) -->
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId> <artifactId>exec-maven-plugin</artifactId>
@ -267,7 +333,7 @@
<includeProjectDependencies>true</includeProjectDependencies> <includeProjectDependencies>true</includeProjectDependencies>
</configuration> </configuration>
</plugin> </plugin>
<!-- Test coverage --> <!-- Coverage report generator -->
<plugin> <plugin>
<groupId>org.jacoco</groupId> <groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId> <artifactId>jacoco-maven-plugin</artifactId>
@ -281,7 +347,7 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<!-- Coveralls data --> <!-- Coverage report uploader -->
<plugin> <plugin>
<groupId>org.eluder.coveralls</groupId> <groupId>org.eluder.coveralls</groupId>
<artifactId>coveralls-maven-plugin</artifactId> <artifactId>coveralls-maven-plugin</artifactId>
@ -289,9 +355,9 @@
<configuration> <configuration>
<failOnServiceError>false</failOnServiceError> <failOnServiceError>false</failOnServiceError>
</configuration> </configuration>
<!-- The secret token is provided by console! --> <!-- The secret token is provided with a command-line parameter! -->
</plugin> </plugin>
<!-- Javadocs settings --> <!-- JavaDocs generator -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
@ -305,9 +371,9 @@
</build> </build>
<repositories> <repositories>
<!-- SpigotMC Repo (Bukkit and SpigotAPI) --> <!-- SpigotAPI Repo -->
<repository> <repository>
<id>spigot-repo</id> <id>spigotmc-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url> <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
</repository> </repository>
@ -319,7 +385,7 @@
<!-- CombatTagPlus Repo --> <!-- CombatTagPlus Repo -->
<repository> <repository>
<id>minelink-thirdparty</id> <id>minelink-repo</id>
<url>http://repo.minelink.net/content/repositories/public</url> <url>http://repo.minelink.net/content/repositories/public</url>
</repository> </repository>
@ -331,33 +397,27 @@
<!-- Multiverse Repo --> <!-- Multiverse Repo -->
<repository> <repository>
<id>onarandombox</id> <id>onarandombox-repo</id>
<url>http://repo.onarandombox.com/content/groups/public</url> <url>http://repo.onarandombox.com/content/groups/public</url>
</repository> </repository>
<!-- Vault Repo --> <!-- Vault Repo -->
<repository> <repository>
<id>vault-repo</id> <id>vault-repo</id>
<url>http://nexus.theyeticave.net/content/repositories/pub_releases</url> <url>http://nexus.hc.to/content/repositories/pub_releases</url>
</repository> </repository>
<!-- XAuth Repo --> <!-- XAuth Repo -->
<repository> <repository>
<id>luricos-releases</id> <id>luricos-repo</id>
<url>http://repo.luricos.de/content/repositories/releases</url> <url>http://repo.luricos.de/content/repositories/releases</url>
</repository> </repository>
<!-- Xephi Repo --> <!-- Our Repo (Many libs) -->
<repository> <repository>
<id>xephi-repo</id> <id>xephi-repo</id>
<url>http://ci.xephi.fr/plugin/repository/everything/</url> <url>http://ci.xephi.fr/plugin/repository/everything/</url>
</repository> </repository>
<!-- PermissionsEx Repo (Re-added, since Xephi's repo was down) -->
<repository>
<id>pex-repo</id>
<url>https://pex-repo.aoeu.xyz/</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
@ -365,7 +425,7 @@
<dependency> <dependency>
<groupId>com.zaxxer</groupId> <groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId> <artifactId>HikariCP</artifactId>
<version>2.4.6</version> <version>2.4.7</version>
<scope>compile</scope> <scope>compile</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>
@ -375,6 +435,7 @@
</exclusions> </exclusions>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- HikariCP Logger -->
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId> <artifactId>slf4j-simple</artifactId>
@ -387,8 +448,7 @@
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId> <artifactId>log4j-core</artifactId>
<!-- Can't use newer versions due to api changes! --> <version>2.5</version>
<version>2.0-beta9</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
@ -401,11 +461,11 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- GSON (required to provide 1.7.10 and below compatibility) --> <!-- GSON (version included in spigot 1.8+, required to provide 1.7.10 and below compatibility) -->
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<version>2.6.2</version> <version>2.2.4</version>
<scope>compile</scope> <scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
@ -453,38 +513,7 @@
</dependency> </dependency>
<!-- Spigot API, http://www.spigotmc.org/ or http://bukkit.org/ --> <!-- Spigot API, http://www.spigotmc.org/ or http://bukkit.org/ -->
<dependency> <!-- Moved in profiles! -->
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${bukkit.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>junit</artifactId>
<groupId>junit</groupId>
</exclusion>
<exclusion>
<artifactId>json-simple</artifactId>
<groupId>com.googlecode.json-simple</groupId>
</exclusion>
<exclusion>
<artifactId>gson</artifactId>
<groupId>com.google.code.gson</groupId>
</exclusion>
<exclusion>
<artifactId>persistence-api</artifactId>
<groupId>javax.persistence</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
<exclusion>
<artifactId>bungeecord-chat</artifactId>
<groupId>net.md-5</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- ProtocolLib, http://dev.bukkit.org/bukkit-plugins/protocollib/ --> <!-- ProtocolLib, http://dev.bukkit.org/bukkit-plugins/protocollib/ -->
<dependency> <dependency>
@ -807,6 +836,13 @@
</exclusions> </exclusions>
</dependency> </dependency>
<!-- Injector -->
<dependency>
<groupId>ch.jalu</groupId>
<artifactId>injector</artifactId>
<version>0.3</version>
</dependency>
<!-- String comparison library. Used for dynamic help system. --> <!-- String comparison library. Used for dynamic help system. -->
<dependency> <dependency>
<groupId>net.ricecode</groupId> <groupId>net.ricecode</groupId>

View File

@ -7,7 +7,7 @@
* verification). Don't forget to update the AUTHME_TABLE value and your * * verification). Don't forget to update the AUTHME_TABLE value and your *
* database credentials in getAuthmeMySqli(). * * database credentials in getAuthmeMySqli(). *
* * * *
* Source: https://github.com/AuthMe-Team/AuthMeReloaded/ * * Source: https://github.com/AuthMe/AuthMeReloaded/ *
*****************************************************************************/ *****************************************************************************/
abstract class AuthMeController { abstract class AuthMeController {
@ -123,4 +123,4 @@ abstract class AuthMeController {
return null; return null;
} }
} }

View File

@ -5,7 +5,7 @@
* ------------------------------------------------------- * * ------------------------------------------------------- *
* See AuthMeController for details. * * See AuthMeController for details. *
* * * *
* Source: https://github.com/AuthMe-Team/AuthMeReloaded/ * * Source: https://github.com/AuthMe/AuthMeReloaded/ *
***********************************************************/ ***********************************************************/
class Bcrypt extends AuthMeController { class Bcrypt extends AuthMeController {

View File

@ -5,7 +5,7 @@
* ------------------------------------------------------- * * ------------------------------------------------------- *
* See AuthMeController for details. * * See AuthMeController for details. *
* * * *
* Source: https://github.com/AuthMe-Team/AuthMeReloaded/ * * Source: https://github.com/AuthMe/AuthMeReloaded/ *
***********************************************************/ ***********************************************************/
class Sha256 extends AuthMeController { class Sha256 extends AuthMeController {

View File

@ -4,7 +4,7 @@ import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages; import fr.xephi.authme.output.Messages;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.permission.PlayerStatePermission;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.ProtectionSettings; import fr.xephi.authme.settings.properties.ProtectionSettings;
import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.BukkitService;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -20,16 +20,16 @@ import static fr.xephi.authme.util.BukkitService.TICKS_PER_SECOND;
*/ */
public class AntiBot { public class AntiBot {
private final NewSetting settings; private final Settings settings;
private final Messages messages; private final Messages messages;
private final PermissionsManager permissionsManager; private final PermissionsManager permissionsManager;
private final BukkitService bukkitService; private final BukkitService bukkitService;
public final CopyOnWriteArrayList<String> antibotKicked = new CopyOnWriteArrayList<String>(); private final CopyOnWriteArrayList<String> antibotKicked = new CopyOnWriteArrayList<String>();
private final CopyOnWriteArrayList<String> antibotPlayers = new CopyOnWriteArrayList<String>(); private final CopyOnWriteArrayList<String> antibotPlayers = new CopyOnWriteArrayList<String>();
private AntiBotStatus antiBotStatus = AntiBotStatus.DISABLED; private AntiBotStatus antiBotStatus = AntiBotStatus.DISABLED;
@Inject @Inject
AntiBot(NewSetting settings, Messages messages, PermissionsManager permissionsManager, AntiBot(Settings settings, Messages messages, PermissionsManager permissionsManager,
BukkitService bukkitService) { BukkitService bukkitService) {
this.settings = settings; this.settings = settings;
this.messages = messages; this.messages = messages;
@ -112,6 +112,27 @@ public class AntiBot {
}, 15 * TICKS_PER_SECOND); }, 15 * TICKS_PER_SECOND);
} }
/**
* Returns whether the player was kicked because of activated antibot. The list is reset
* when antibot is deactivated.
*
* @param name the name to check
* @return true if the given name has been kicked because of Antibot
*/
public boolean wasPlayerKicked(String name) {
return antibotKicked.contains(name.toLowerCase());
}
/**
* Adds a name to the list of players kicked by antibot. Should only be used when a player
* is determined to be kicked because of failed antibot verification.
*
* @param name the name to add
*/
public void addPlayerKick(String name) {
antibotKicked.addIfAbsent(name.toLowerCase());
}
public enum AntiBotStatus { public enum AntiBotStatus {
LISTENING, LISTENING,
DISABLED, DISABLED,

View File

@ -1,146 +1,95 @@
package fr.xephi.authme; package fr.xephi.authme;
import ch.jalu.injector.Injector;
import ch.jalu.injector.InjectorBuilder;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.api.API; import fr.xephi.authme.api.API;
import fr.xephi.authme.api.NewAPI; import fr.xephi.authme.api.NewAPI;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.cache.backup.JsonCache;
import fr.xephi.authme.cache.limbo.LimboCache;
import fr.xephi.authme.cache.limbo.LimboPlayer;
import fr.xephi.authme.command.CommandHandler; import fr.xephi.authme.command.CommandHandler;
import fr.xephi.authme.datasource.CacheDataSource;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.datasource.FlatFile;
import fr.xephi.authme.datasource.MySQL;
import fr.xephi.authme.datasource.SQLite;
import fr.xephi.authme.hooks.BungeeCordMessage;
import fr.xephi.authme.hooks.PluginHooks;
import fr.xephi.authme.initialization.AuthMeServiceInitializer;
import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.initialization.MetricsStarter; import fr.xephi.authme.initialization.Initializer;
import fr.xephi.authme.listener.AuthMeBlockListener; import fr.xephi.authme.initialization.MetricsManager;
import fr.xephi.authme.listener.AuthMeEntityListener; import fr.xephi.authme.initialization.OnShutdownPlayerSaver;
import fr.xephi.authme.listener.AuthMeInventoryPacketAdapter; import fr.xephi.authme.initialization.TaskCloser;
import fr.xephi.authme.listener.AuthMePlayerListener; import fr.xephi.authme.listener.BlockListener;
import fr.xephi.authme.listener.AuthMePlayerListener16; import fr.xephi.authme.listener.EntityListener;
import fr.xephi.authme.listener.AuthMePlayerListener18; import fr.xephi.authme.listener.PlayerListener;
import fr.xephi.authme.listener.AuthMeServerListener; import fr.xephi.authme.listener.PlayerListener16;
import fr.xephi.authme.listener.AuthMeTabCompletePacketAdapter; import fr.xephi.authme.listener.PlayerListener18;
import fr.xephi.authme.listener.AuthMeTablistPacketAdapter; import fr.xephi.authme.listener.PlayerListener19;
import fr.xephi.authme.mail.SendMailSSL; import fr.xephi.authme.listener.ServerListener;
import fr.xephi.authme.output.ConsoleFilter;
import fr.xephi.authme.output.Log4JFilter;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages; import fr.xephi.authme.output.Messages;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.Management; import fr.xephi.authme.permission.PermissionsSystemType;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.SHA256; import fr.xephi.authme.security.crypts.SHA256;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SettingsMigrationService;
import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.PurgeSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.settings.properties.SettingsFieldRetriever; import fr.xephi.authme.task.CleanupTask;
import fr.xephi.authme.settings.propertymap.PropertyMap; import fr.xephi.authme.task.purge.PurgeService;
import fr.xephi.authme.task.PurgeTask;
import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.CollectionUtils;
import fr.xephi.authme.util.FileUtils;
import fr.xephi.authme.util.GeoLiteAPI; import fr.xephi.authme.util.GeoLiteAPI;
import fr.xephi.authme.util.MigrationService; import fr.xephi.authme.util.MigrationService;
import fr.xephi.authme.util.StringUtils;
import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginLoader;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import java.io.File; import java.io.File;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_ACCOUNT; import static fr.xephi.authme.util.BukkitService.TICKS_PER_MINUTE;
import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_PASSWORD; import static fr.xephi.authme.util.Utils.isClassLoaded;
import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS;
/** /**
* The AuthMe main class. * The AuthMe main class.
*/ */
public class AuthMe extends JavaPlugin { public class AuthMe extends JavaPlugin {
// Defines the name of the plugin. // Constants
private static final String PLUGIN_NAME = "AuthMeReloaded"; private static final String PLUGIN_NAME = "AuthMeReloaded";
private static final String LOG_FILENAME = "authme.log";
private static final int CLEANUP_INTERVAL = 5 * TICKS_PER_MINUTE;
// Default version and build number values; // Default version and build number values;
private static String pluginVersion = "N/D"; private static String pluginVersion = "N/D";
private static String pluginBuildNumber = "Unknown"; private static String pluginBuildNumber = "Unknown";
// Private Instances // Private instances
private static AuthMe plugin;
/*
* Maps and stuff
*/
public final ConcurrentHashMap<String, BukkitTask> sessions = new ConcurrentHashMap<>();
/*
* Public instances
*/
public NewAPI api;
// TODO #655: Encapsulate mail
public SendMailSSL mail;
// TODO #656: Encapsulate data manager
public DataManager dataManager;
/*
* Private instances
*/
// TODO #604: Encapsulate ProtocolLib members
public AuthMeInventoryPacketAdapter inventoryProtector;
public AuthMeTabCompletePacketAdapter tabComplete;
public AuthMeTablistPacketAdapter tablistHider;
private Management management;
private CommandHandler commandHandler; private CommandHandler commandHandler;
private PermissionsManager permsMan; private PermissionsManager permsMan;
private NewSetting newSettings; private Settings settings;
private Messages messages; private Messages messages;
private JsonCache playerBackup;
private PasswordSecurity passwordSecurity;
private DataSource database; private DataSource database;
private PluginHooks pluginHooks;
private SpawnLoader spawnLoader;
private boolean autoPurging;
private BukkitService bukkitService; private BukkitService bukkitService;
private AuthMeServiceInitializer initializer; private Injector injector;
private GeoLiteAPI geoLiteApi;
private PlayerCache playerCache;
/** /**
* Get the plugin's instance. * Constructor.
*
* @return AuthMe
*/ */
@Deprecated public AuthMe() {
public static AuthMe getInstance() { }
return plugin;
/*
* Constructor for unit testing.
*/
@VisibleForTesting
@SuppressWarnings("deprecation") // the super constructor is deprecated to mark it for unit testing only
protected AuthMe(final PluginLoader loader, final Server server, final PluginDescriptionFile description,
final File dataFolder, final File file) {
super(loader, server, description, dataFolder, file);
} }
/** /**
@ -170,8 +119,100 @@ public class AuthMe extends JavaPlugin {
return pluginBuildNumber; return pluginBuildNumber;
} }
/**
* Method called when the server enables the plugin.
*/
@Override
public void onEnable() {
try {
initializeServices();
} catch (Exception e) {
ConsoleLogger.logException("Aborting initialization of AuthMe:", e);
stopOrUnload();
return;
}
// Show settings warnings
showSettingsWarnings();
// If server is using PermissionsBukkit, print a warning that some features may not be supported
if (PermissionsSystemType.PERMISSIONS_BUKKIT.equals(permsMan.getPermissionSystem())) {
ConsoleLogger.warning("Warning! This server uses PermissionsBukkit for permissions. Some permissions features may not be supported!");
}
// Do a backup on start
new PerformBackup(this, settings).doBackup(PerformBackup.BackupCause.START);
// Set up Metrics
MetricsManager.sendMetrics(this, settings);
// Sponsor messages
ConsoleLogger.info("Development builds are available on our jenkins, thanks to f14stelt.");
ConsoleLogger.info("Do you want a good game server? Look at our sponsor GameHosting.it leader in Italy as Game Server Provider!");
// Successful message
ConsoleLogger.info("AuthMe " + getPluginVersion() + " build n°" + getPluginBuildNumber() + " correctly enabled!");
// Purge on start if enabled
PurgeService purgeService = injector.getSingleton(PurgeService.class);
purgeService.runAutoPurge();
// Schedule clean up task
CleanupTask cleanupTask = injector.getSingleton(CleanupTask.class);
cleanupTask.runTaskTimerAsynchronously(this, CLEANUP_INTERVAL, CLEANUP_INTERVAL);
}
private void initializeServices() throws Exception {
// Set the plugin instance and load plugin info from the plugin description.
loadPluginInfo();
// Set the Logger instance and log file path
ConsoleLogger.setLogger(getLogger());
ConsoleLogger.setLogFile(new File(getDataFolder(), LOG_FILENAME));
bukkitService = new BukkitService(this);
Initializer initializer = new Initializer(this, bukkitService);
// Load settings and set up the console and console filter
settings = initializer.createSettings();
ConsoleLogger.setLoggingOptions(settings);
initializer.setupConsoleFilter(settings, getLogger());
// Connect to the database and set up tables
database = initializer.setupDatabase(settings);
// Convert deprecated PLAINTEXT hash entries
MigrationService.changePlainTextToSha256(settings, database, new SHA256());
// Injector initialization
injector = new InjectorBuilder().addDefaultHandlers("fr.xephi.authme").create();
// Register elements of the Bukkit / JavaPlugin environment
injector.register(AuthMe.class, this);
injector.register(Server.class, getServer());
injector.register(PluginManager.class, getServer().getPluginManager());
injector.register(BukkitScheduler.class, getServer().getScheduler());
injector.provide(DataFolder.class, getDataFolder());
// Register elements we instantiate manually
injector.register(Settings.class, settings);
injector.register(DataSource.class, database);
injector.register(BukkitService.class, bukkitService);
instantiateServices(injector);
// Reload support hook
reloadSupportHook();
// Register event listeners
registerEventListeners(injector);
// Start Email recall task if needed
initializer.scheduleRecallEmailTask(settings, database, messages);
}
// Get version and build number of the plugin // Get version and build number of the plugin
private void setPluginInfos() { private void loadPluginInfo() {
String versionRaw = this.getDescription().getVersion(); String versionRaw = this.getDescription().getVersion();
int index = versionRaw.lastIndexOf("-"); int index = versionRaw.lastIndexOf("-");
if (index != -1) { if (index != -1) {
@ -184,136 +225,24 @@ public class AuthMe extends JavaPlugin {
} }
/** /**
* Method called when the server enables the plugin. * Instantiates all services.
*
* @param injector the injector
*/ */
@Override protected void instantiateServices(Injector injector) {
public void onEnable() { // PlayerCache is still injected statically sometimes
// Set various instances playerCache = PlayerCache.getInstance();
plugin = this; injector.register(PlayerCache.class, playerCache);
ConsoleLogger.setLogger(getLogger());
setPluginInfos();
// Load settings and custom configurations, if it fails, stop the server due to security reasons. messages = injector.getSingleton(Messages.class);
newSettings = createNewSetting(); permsMan = injector.getSingleton(PermissionsManager.class);
if (newSettings == null) { bukkitService = injector.getSingleton(BukkitService.class);
getLogger().warning("Could not load configuration. Aborting."); commandHandler = injector.getSingleton(CommandHandler.class);
getServer().shutdown(); geoLiteApi = injector.getSingleton(GeoLiteAPI.class);
return;
}
ConsoleLogger.setLogFile(new File(getDataFolder(), "authme.log"));
ConsoleLogger.setLoggingOptions(newSettings);
// Old settings manager // Trigger construction of API classes; they will keep track of the singleton
if (!loadSettings()) { injector.getSingleton(NewAPI.class);
getServer().shutdown(); injector.getSingleton(API.class);
setEnabled(false);
return;
}
messages = new Messages(newSettings.getMessagesFile(), newSettings.getDefaultMessagesFile());
// Connect to the database and setup tables
try {
setupDatabase(newSettings);
} catch (Exception e) {
ConsoleLogger.logException("Fatal error occurred during database connection! "
+ "Authme initialization aborted!", e);
stopOrUnload();
return;
}
MigrationService.changePlainTextToSha256(newSettings, database, new SHA256());
initializer = new AuthMeServiceInitializer("fr.xephi.authme");
// Register elements of the Bukkit / JavaPlugin environment
initializer.register(AuthMe.class, this);
initializer.register(Server.class, getServer());
initializer.register(PluginManager.class, getServer().getPluginManager());
initializer.register(BukkitScheduler.class, getServer().getScheduler());
initializer.provide(DataFolder.class, getDataFolder());
// Register elements we instantiate manually
initializer.register(NewSetting.class, newSettings);
initializer.register(Messages.class, messages);
initializer.register(DataSource.class, database);
// Some statically injected things
initializer.register(PlayerCache.class, PlayerCache.getInstance());
// Note ljacqu 20160612: Instantiate LimboCache first to make sure it is instantiated
// (because sometimes it's used via LimboCache.getInstance())
// Once LimboCache#getInstance() no longer exists this can be removed!
initializer.get(LimboCache.class);
permsMan = initializer.get(PermissionsManager.class);
bukkitService = initializer.get(BukkitService.class);
pluginHooks = initializer.get(PluginHooks.class);
passwordSecurity = initializer.get(PasswordSecurity.class);
spawnLoader = initializer.get(SpawnLoader.class);
commandHandler = initializer.get(CommandHandler.class);
api = initializer.get(NewAPI.class);
management = initializer.get(Management.class);
dataManager = initializer.get(DataManager.class);
initializer.get(API.class);
// Set up Metrics
MetricsStarter.setupMetrics(this, newSettings);
// Set console filter
setupConsoleFilter();
// Download and load GeoIp.dat file if absent
GeoLiteAPI.isDataAvailable();
// Set up the mail API
setupMailApi();
// Check if the ProtocolLib is available. If so we could listen for
// inventory protection
checkProtocolLib();
// End of Hooks
// Do a backup on start
new PerformBackup(this, newSettings).doBackup(PerformBackup.BackupCause.START);
// Setup the inventory backup
playerBackup = new JsonCache();
// Set up the BungeeCord hook
setupBungeeCordHook(newSettings, initializer);
// Reload support hook
reloadSupportHook();
// Register event listeners
registerEventListeners(initializer);
// Start Email recall task if needed
scheduleRecallEmailTask();
// Show settings warnings
showSettingsWarnings();
// Sponsor messages
ConsoleLogger.info("Development builds are available on our jenkins, thanks to f14stelt.");
ConsoleLogger.info("Do you want a good game server? Look at our sponsor GameHosting.it leader in Italy as Game Server Provider!");
// Successful message
ConsoleLogger.info("AuthMe " + this.getDescription().getVersion() + " correctly enabled!");
// Purge on start if enabled
runAutoPurge();
}
/**
* Set up the mail API, if enabled.
*/
private void setupMailApi() {
// Make sure the mail API is enabled
if (!newSettings.getProperty(MAIL_ACCOUNT).isEmpty() && !newSettings.getProperty(MAIL_PASSWORD).isEmpty()) {
this.mail = new SendMailSSL(this, newSettings);
}
} }
/** /**
@ -321,370 +250,105 @@ public class AuthMe extends JavaPlugin {
*/ */
private void showSettingsWarnings() { private void showSettingsWarnings() {
// Force single session disabled // Force single session disabled
if (!newSettings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)) { if (!settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)) {
ConsoleLogger.showError("WARNING!!! By disabling ForceSingleSession, your server protection is inadequate!"); ConsoleLogger.warning("WARNING!!! By disabling ForceSingleSession, your server protection is inadequate!");
} }
// Session timeout disabled // Session timeout disabled
if (newSettings.getProperty(PluginSettings.SESSIONS_TIMEOUT) == 0 if (settings.getProperty(PluginSettings.SESSIONS_TIMEOUT) == 0
&& newSettings.getProperty(PluginSettings.SESSIONS_ENABLED)) { && settings.getProperty(PluginSettings.SESSIONS_ENABLED)) {
ConsoleLogger.showError("WARNING!!! You set session timeout to 0, this may cause security issues!"); ConsoleLogger.warning("WARNING!!! You set session timeout to 0, this may cause security issues!");
} }
} }
/** /**
* Register all event listeners. * Registers all event listeners.
*
* @param injector the injector
*/ */
private void registerEventListeners(AuthMeServiceInitializer initializer) { protected void registerEventListeners(Injector injector) {
// Get the plugin manager instance // Get the plugin manager instance
PluginManager pluginManager = getServer().getPluginManager(); PluginManager pluginManager = getServer().getPluginManager();
// Register event listeners // Register event listeners
pluginManager.registerEvents(initializer.get(AuthMePlayerListener.class), this); pluginManager.registerEvents(injector.getSingleton(PlayerListener.class), this);
pluginManager.registerEvents(initializer.get(AuthMeBlockListener.class), this); pluginManager.registerEvents(injector.getSingleton(BlockListener.class), this);
pluginManager.registerEvents(initializer.get(AuthMeEntityListener.class), this); pluginManager.registerEvents(injector.getSingleton(EntityListener.class), this);
pluginManager.registerEvents(initializer.get(AuthMeServerListener.class), this); pluginManager.registerEvents(injector.getSingleton(ServerListener.class), this);
// Try to register 1.6 player listeners // Try to register 1.6 player listeners
try { if (isClassLoaded("org.bukkit.event.player.PlayerEditBookEvent")) {
Class.forName("org.bukkit.event.player.PlayerEditBookEvent"); pluginManager.registerEvents(injector.getSingleton(PlayerListener16.class), this);
pluginManager.registerEvents(initializer.get(AuthMePlayerListener16.class), this);
} catch (ClassNotFoundException ignore) {
} }
// Try to register 1.8 player listeners // Try to register 1.8 player listeners
try { if (isClassLoaded("org.bukkit.event.player.PlayerInteractAtEntityEvent")) {
Class.forName("org.bukkit.event.player.PlayerInteractAtEntityEvent"); pluginManager.registerEvents(injector.getSingleton(PlayerListener18.class), this);
pluginManager.registerEvents(initializer.get(AuthMePlayerListener18.class), this); }
} catch (ClassNotFoundException ignore) {
// Try to register 1.9 player listeners
if (isClassLoaded("org.bukkit.event.player.PlayerSwapHandItemsEvent")) {
pluginManager.registerEvents(injector.getSingleton(PlayerListener19.class), this);
} }
} }
/**
* Stops the server or disables the plugin, as defined in the configuration.
*/
public void stopOrUnload() {
if (settings == null || settings.getProperty(SecuritySettings.STOP_SERVER_ON_PROBLEM)) {
ConsoleLogger.warning("THE SERVER IS GOING TO SHUT DOWN AS DEFINED IN THE CONFIGURATION!");
setEnabled(false);
getServer().shutdown();
} else {
setEnabled(false);
}
}
// TODO: check this, do we really need it? -sgdc3
private void reloadSupportHook() { private void reloadSupportHook() {
if (database != null) { if (database != null) {
int playersOnline = bukkitService.getOnlinePlayers().size(); int playersOnline = bukkitService.getOnlinePlayers().size();
if (playersOnline < 1) { if (playersOnline == 0) {
database.purgeLogged(); database.purgeLogged();
} else if (Settings.reloadSupport) { } else if (settings.getProperty(SecuritySettings.USE_RELOAD_COMMAND_SUPPORT)) {
for (PlayerAuth auth : database.getLoggedPlayers()) { for (PlayerAuth auth : database.getLoggedPlayers()) {
if (auth == null) { if (auth != null) {
continue; auth.setLastLogin(new Date().getTime());
database.updateSession(auth);
playerCache.addPlayer(auth);
} }
auth.setLastLogin(new Date().getTime());
database.updateSession(auth);
PlayerCache.getInstance().addPlayer(auth);
} }
} }
} }
} }
/**
* Set up the BungeeCord hook.
*/
private void setupBungeeCordHook(NewSetting settings, AuthMeServiceInitializer initializer) {
if (settings.getProperty(HooksSettings.BUNGEECORD)) {
Bukkit.getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
Bukkit.getMessenger().registerIncomingPluginChannel(
this, "BungeeCord", initializer.get(BungeeCordMessage.class));
}
}
/**
* Load the plugin's settings.
*
* @return True on success, false on failure.
*/
private boolean loadSettings() {
try {
new Settings(this);
return true;
} catch (Exception e) {
ConsoleLogger.logException("Can't load the configuration file... Something went wrong. "
+ "To avoid security issues the server will shut down!", e);
getServer().shutdown();
}
return false;
}
private NewSetting createNewSetting() {
File configFile = new File(getDataFolder(), "config.yml");
PropertyMap properties = SettingsFieldRetriever.getAllPropertyFields();
SettingsMigrationService migrationService = new SettingsMigrationService();
return FileUtils.copyFileFromResource(configFile, "config.yml")
? new NewSetting(configFile, getDataFolder(), properties, migrationService)
: null;
}
/**
* Set up the console filter.
*/
private void setupConsoleFilter() {
if (newSettings.getProperty(SecuritySettings.REMOVE_PASSWORD_FROM_CONSOLE)) {
ConsoleFilter filter = new ConsoleFilter();
getLogger().setFilter(filter);
Bukkit.getLogger().setFilter(filter);
Logger.getLogger("Minecraft").setFilter(filter);
// Set Log4J Filter
try {
Class.forName("org.apache.logging.log4j.core.Filter");
setLog4JFilter();
} catch (ClassNotFoundException | NoClassDefFoundError e) {
ConsoleLogger.info("You're using Minecraft 1.6.x or older, Log4J support will be disabled");
}
}
}
@Override @Override
public void onDisable() { public void onDisable() {
// Save player data // onDisable is also called when we prematurely abort, so any field may be null
BukkitService bukkitService = initializer.getIfAvailable(BukkitService.class); OnShutdownPlayerSaver onShutdownPlayerSaver = injector == null
LimboCache limboCache = initializer.getIfAvailable(LimboCache.class); ? null
: injector.createIfHasDependencies(OnShutdownPlayerSaver.class);
if (bukkitService != null && limboCache != null) { if (onShutdownPlayerSaver != null) {
Collection<? extends Player> players = bukkitService.getOnlinePlayers(); onShutdownPlayerSaver.saveAllPlayers();
for (Player player : players) {
savePlayer(player, limboCache);
}
} }
// Do backup on stop if enabled // Do backup on stop if enabled
if (newSettings != null) { if (settings != null) {
new PerformBackup(this, newSettings).doBackup(PerformBackup.BackupCause.STOP); new PerformBackup(this, settings).doBackup(PerformBackup.BackupCause.STOP);
} }
final AuthMe pluginInstance = this;
new Thread(new Runnable() {
@Override
public void run() {
List<Integer> pendingTasks = new ArrayList<>();
for (BukkitTask pendingTask : getServer().getScheduler().getPendingTasks()) {
if (pendingTask.getOwner().equals(pluginInstance) && !pendingTask.isSync()) {
pendingTasks.add(pendingTask.getTaskId());
}
}
getLogger().info("Waiting for " + pendingTasks.size() + " tasks to finish");
int progress = 0;
for (int taskId : pendingTasks) {
int maxTries = 5;
while (getServer().getScheduler().isCurrentlyRunning(taskId)) {
if (maxTries <= 0) {
getLogger().info("Async task " + taskId + " times out after to many tries");
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
maxTries--;
}
progress++; // Wait for tasks and close data source
getLogger().info("Progress: " + progress + " / " + pendingTasks.size()); new Thread(
} new TaskCloser(this, database),
if (database != null) { "AuthMe-DataSource#close"
database.close(); ).start();
}
}
}, "AuthMe-DataSource#close").start();
// Disabled correctly // Disabled correctly
ConsoleLogger.info("AuthMe " + this.getDescription().getVersion() + " disabled!"); ConsoleLogger.info("AuthMe " + this.getDescription().getVersion() + " disabled!");
ConsoleLogger.close(); ConsoleLogger.close();
} }
// Stop/unload the server/plugin as defined in the configuration
public void stopOrUnload() {
if (Settings.isStopEnabled) {
ConsoleLogger.showError("THE SERVER IS GOING TO SHUT DOWN AS DEFINED IN THE CONFIGURATION!");
getServer().shutdown();
} else {
getServer().getPluginManager().disablePlugin(AuthMe.getInstance());
}
}
/**
* Sets up the data source.
*
* @param settings The settings instance
*
* @throws ClassNotFoundException if no driver could be found for the datasource
* @throws SQLException when initialization of a SQL datasource failed
* @see AuthMe#database
*/
public void setupDatabase(NewSetting settings) throws ClassNotFoundException, SQLException {
if (this.database != null) {
this.database.close();
}
DataSourceType dataSourceType = settings.getProperty(DatabaseSettings.BACKEND);
DataSource dataSource;
switch (dataSourceType) {
case FILE:
dataSource = new FlatFile();
break;
case MYSQL:
dataSource = new MySQL(settings);
break;
case SQLITE:
dataSource = new SQLite(settings);
break;
default:
throw new UnsupportedOperationException("Unknown data source type '" + dataSourceType + "'");
}
DataSource convertedSource = MigrationService.convertFlatfileToSqlite(newSettings, dataSource);
dataSource = convertedSource == null ? dataSource : convertedSource;
if (newSettings.getProperty(DatabaseSettings.USE_CACHING)) {
dataSource = new CacheDataSource(dataSource);
}
database = dataSource;
if (DataSourceType.SQLITE == dataSourceType) {
getServer().getScheduler().runTaskAsynchronously(this, new Runnable() {
@Override
public void run() {
int accounts = database.getAccountsRegistered();
if (accounts >= 4000) {
ConsoleLogger.showError("YOU'RE USING THE SQLITE DATABASE WITH "
+ accounts + "+ ACCOUNTS; FOR BETTER PERFORMANCE, PLEASE UPGRADE TO MYSQL!!");
}
}
});
}
}
// Set the console filter to remove the passwords
private void setLog4JFilter() {
Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
@Override
public void run() {
org.apache.logging.log4j.core.Logger logger;
logger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger();
logger.addFilter(new Log4JFilter());
logger = (org.apache.logging.log4j.core.Logger) LogManager.getLogger("net.minecraft");
logger.addFilter(new Log4JFilter());
}
});
}
// Check the presence of the ProtocolLib plugin
public void checkProtocolLib() {
if (!getServer().getPluginManager().isPluginEnabled("ProtocolLib")) {
if (newSettings.getProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN)) {
ConsoleLogger.showError("WARNING! The protectInventory feature requires ProtocolLib! Disabling it...");
Settings.protectInventoryBeforeLogInEnabled = false;
newSettings.setProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN, false);
newSettings.save();
}
return;
}
if (newSettings.getProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN) && inventoryProtector == null) {
inventoryProtector = new AuthMeInventoryPacketAdapter(this);
inventoryProtector.register();
} else if (inventoryProtector != null) {
inventoryProtector.unregister();
inventoryProtector = null;
}
if (newSettings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN) && tabComplete == null) {
tabComplete = new AuthMeTabCompletePacketAdapter(this);
tabComplete.register();
} else if (tabComplete != null) {
tabComplete.unregister();
tabComplete = null;
}
if (newSettings.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN) && tablistHider == null) {
tablistHider = new AuthMeTablistPacketAdapter(this, bukkitService);
tablistHider.register();
} else if (tablistHider != null) {
tablistHider.unregister();
tablistHider = null;
}
}
// Save Player Data
private void savePlayer(Player player, LimboCache limboCache) {
if (safeIsNpc(player) || Utils.isUnrestricted(player)) {
return;
}
String name = player.getName().toLowerCase();
if (PlayerCache.getInstance().isAuthenticated(name) && !player.isDead() && Settings.isSaveQuitLocationEnabled) {
final PlayerAuth auth = PlayerAuth.builder()
.name(player.getName().toLowerCase())
.realName(player.getName())
.location(player.getLocation()).build();
database.updateQuitLoc(auth);
}
if (limboCache.hasLimboPlayer(name)) {
LimboPlayer limbo = limboCache.getLimboPlayer(name);
if (!Settings.noTeleport) {
player.teleport(limbo.getLoc());
}
Utils.addNormal(player, limbo.getGroup());
player.setOp(limbo.isOperator());
limbo.getTimeoutTask().cancel();
limboCache.deleteLimboPlayer(name);
if (this.playerBackup.doesCacheExist(player)) {
this.playerBackup.removeCache(player);
}
}
PlayerCache.getInstance().removePlayer(name);
}
private boolean safeIsNpc(Player player) {
return pluginHooks != null && pluginHooks.isNpc(player) || player.hasMetadata("NPC");
}
// Purge inactive players from the database, as defined in the configuration
private void runAutoPurge() {
if (!newSettings.getProperty(PurgeSettings.USE_AUTO_PURGE) || autoPurging) {
return;
}
autoPurging = true;
ConsoleLogger.info("AutoPurging the Database...");
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, -newSettings.getProperty(PurgeSettings.DAYS_BEFORE_REMOVE_PLAYER));
long until = calendar.getTimeInMillis();
Set<String> cleared = database.autoPurgeDatabase(until);
if (CollectionUtils.isEmpty(cleared)) {
return;
}
ConsoleLogger.info("AutoPurging the Database: " + cleared.size() + " accounts removed!");
ConsoleLogger.info("Purging user accounts...");
new PurgeTask(plugin, Bukkit.getConsoleSender(), cleared, true, Bukkit.getOfflinePlayers())
.runTaskTimer(plugin, 0, 1);
}
// Return the spawn location of a player
@Deprecated
public Location getSpawnLocation(Player player) {
return spawnLoader.getSpawnLocation(player);
}
private void scheduleRecallEmailTask() {
if (!newSettings.getProperty(RECALL_PLAYERS)) {
return;
}
Bukkit.getScheduler().runTaskTimerAsynchronously(this, new Runnable() {
@Override
public void run() {
for (PlayerAuth auth : database.getLoggedPlayers()) {
String email = auth.getEmail();
if (StringUtils.isEmpty(email) || "your@email.com".equalsIgnoreCase(email)) {
Player player = bukkitService.getPlayerExact(auth.getRealName());
if (player != null) {
messages.send(player, MessageKey.ADD_EMAIL_MESSAGE);
}
}
}
}
}, 1, 1200 * newSettings.getProperty(EmailSettings.DELAY_RECALL));
}
public String replaceAllInfo(String message, Player player) { public String replaceAllInfo(String message, Player player) {
String playersOnline = Integer.toString(bukkitService.getOnlinePlayers().size()); String playersOnline = Integer.toString(bukkitService.getOnlinePlayers().size());
String ipAddress = Utils.getPlayerIp(player); String ipAddress = Utils.getPlayerIp(player);
@ -695,15 +359,15 @@ public class AuthMe extends JavaPlugin {
.replace("{ONLINE}", playersOnline) .replace("{ONLINE}", playersOnline)
.replace("{MAXPLAYERS}", Integer.toString(server.getMaxPlayers())) .replace("{MAXPLAYERS}", Integer.toString(server.getMaxPlayers()))
.replace("{IP}", ipAddress) .replace("{IP}", ipAddress)
.replace("{LOGINS}", Integer.toString(PlayerCache.getInstance().getLogged())) .replace("{LOGINS}", Integer.toString(playerCache.getLogged()))
.replace("{WORLD}", player.getWorld().getName()) .replace("{WORLD}", player.getWorld().getName())
.replace("{SERVER}", server.getServerName()) .replace("{SERVER}", server.getServerName())
.replace("{VERSION}", server.getBukkitVersion()) .replace("{VERSION}", server.getBukkitVersion())
.replace("{COUNTRY}", GeoLiteAPI.getCountryName(ipAddress)); // TODO: We should cache info like this, maybe with a class that extends Player?
.replace("{COUNTRY}", geoLiteApi.getCountryName(ipAddress));
} }
/** /**
* Handle Bukkit commands. * Handle Bukkit commands.
* *
@ -726,67 +390,4 @@ public class AuthMe extends JavaPlugin {
// Handle the command // Handle the command
return commandHandler.processCommand(sender, commandLabel, args); return commandHandler.processCommand(sender, commandLabel, args);
} }
public void notifyAutoPurgeEnd() {
this.autoPurging = false;
}
// -------------
// Service getters (deprecated)
// Use @Inject fields instead
// -------------
/**
* @return NewSetting
* @deprecated should be used in API classes only (temporarily)
*/
@Deprecated
public NewSetting getSettings() {
return newSettings;
}
/**
* @return permission manager
* @deprecated should be used in API classes only (temporarily)
*/
@Deprecated
public PermissionsManager getPermissionsManager() {
return this.permsMan;
}
/**
* @return process manager
* @deprecated should be used in API classes only (temporarily)
*/
@Deprecated
public Management getManagement() {
return management;
}
/**
* @return the datasource
* @deprecated should be used in API classes only (temporarily)
*/
@Deprecated
public DataSource getDataSource() {
return database;
}
/**
* @return password manager
* @deprecated should be used in API classes only (temporarily)
*/
@Deprecated
public PasswordSecurity getPasswordSecurity() {
return passwordSecurity;
}
/**
* @return plugin hooks
* @deprecated should be used in API classes only (temporarily)
*/
@Deprecated
public PluginHooks getPluginHooks() {
return pluginHooks;
}
} }

View File

@ -1,7 +1,9 @@
package fr.xephi.authme; package fr.xephi.authme;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.output.LogLevel;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.StringUtils;
@ -11,7 +13,6 @@ import java.io.IOException;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**
@ -22,7 +23,7 @@ public final class ConsoleLogger {
private static final String NEW_LINE = System.getProperty("line.separator"); private static final String NEW_LINE = System.getProperty("line.separator");
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("[MM-dd HH:mm:ss]"); private static final DateFormat DATE_FORMAT = new SimpleDateFormat("[MM-dd HH:mm:ss]");
private static Logger logger; private static Logger logger;
private static boolean enableDebug = false; private static LogLevel logLevel = LogLevel.INFO;
private static boolean useLogging = false; private static boolean useLogging = false;
private static File logFile; private static File logFile;
private static FileWriter fileWriter; private static FileWriter fileWriter;
@ -30,17 +31,36 @@ public final class ConsoleLogger {
private ConsoleLogger() { private ConsoleLogger() {
} }
// --------
// Configurations
// --------
/**
* Set the logger to use.
*
* @param logger The logger
*/
public static void setLogger(Logger logger) { public static void setLogger(Logger logger) {
ConsoleLogger.logger = logger; ConsoleLogger.logger = logger;
} }
/**
* Set the file to log to if enabled.
*
* @param logFile The log file
*/
public static void setLogFile(File logFile) { public static void setLogFile(File logFile) {
ConsoleLogger.logFile = logFile; ConsoleLogger.logFile = logFile;
} }
public static void setLoggingOptions(NewSetting settings) { /**
* Load the required settings.
*
* @param settings The settings instance
*/
public static void setLoggingOptions(Settings settings) {
ConsoleLogger.logLevel = settings.getProperty(PluginSettings.LOG_LEVEL);
ConsoleLogger.useLogging = settings.getProperty(SecuritySettings.USE_LOGGING); ConsoleLogger.useLogging = settings.getProperty(SecuritySettings.USE_LOGGING);
ConsoleLogger.enableDebug = !settings.getProperty(SecuritySettings.REMOVE_SPAM_FROM_CONSOLE);
if (useLogging) { if (useLogging) {
if (fileWriter == null) { if (fileWriter == null) {
try { try {
@ -54,86 +74,81 @@ public final class ConsoleLogger {
} }
} }
// --------
// Logging methods
// --------
/** /**
* Print an info message. * Log a WARN message.
* *
* @param message String * @param message The message to log
*/
public static void warning(String message) {
logger.warning(message);
writeLog("[WARN] " + message);
}
/**
* Log an INFO message.
*
* @param message The message to log
*/ */
public static void info(String message) { public static void info(String message) {
logger.info(message); logger.info(message);
if (useLogging) { writeLog("[INFO] " + message);
writeLog(message);
}
} }
/**
* Log a FINE message if enabled.
* <p>
* Implementation note: this logs a message on INFO level because
* levels below INFO are disabled by Bukkit/Spigot.
*
* @param message The message to log
*/
public static void fine(String message) {
if (logLevel.includes(LogLevel.FINE)) {
logger.info(message);
writeLog("[FINE] " + message);
}
}
/**
* Log a DEBUG message if enabled.
* <p>
* Implementation note: this logs a message on INFO level and prefixes it with "DEBUG" because
* levels below INFO are disabled by Bukkit/Spigot.
*
* @param message The message to log
*/
public static void debug(String message) { public static void debug(String message) {
if (enableDebug) { if (logLevel.includes(LogLevel.DEBUG)) {
//creating and filling an exception is a expensive call logger.info("Debug: " + message);
//TODO #419 20160601: ->so it should be removed as soon #419 is fixed writeLog("[DEBUG] " + message);
//logger.isLoggable does not work because the plugin logger is always ALL
logger.log(Level.FINE, message + ' ' + Thread.currentThread().getName(), new Exception());
if (useLogging) {
writeLog("Debug: " + Thread.currentThread().getName() + ':' + message);
}
} }
} }
/** /**
* Print an error message. * Log a Throwable with the provided message on WARNING level
* * and save the stack trace to the log file.
* @param message String
*/
public static void showError(String message) {
logger.warning(message);
if (useLogging) {
writeLog("ERROR: " + message);
}
}
/**
* Write a message into the log file with a TimeStamp.
*
* @param message String
*/
private static void writeLog(String message) {
String dateTime;
synchronized (DATE_FORMAT) {
dateTime = DATE_FORMAT.format(new Date());
}
try {
fileWriter.write(dateTime);
fileWriter.write(": ");
fileWriter.write(message);
fileWriter.write(NEW_LINE);
fileWriter.flush();
} catch (IOException ignored) {
}
}
/**
* Write a StackTrace into the log.
*
* @param th The Throwable whose stack trace should be logged
*/
public static void writeStackTrace(Throwable th) {
if (useLogging) {
writeLog(Throwables.getStackTraceAsString(th));
}
}
/**
* Logs a Throwable with the provided message and saves the stack trace to the log file.
* *
* @param message The message to accompany the exception * @param message The message to accompany the exception
* @param th The Throwable to log * @param th The Throwable to log
*/ */
public static void logException(String message, Throwable th) { public static void logException(String message, Throwable th) {
showError(message + " " + StringUtils.formatException(th)); warning(message + " " + StringUtils.formatException(th));
writeStackTrace(th); writeLog(Throwables.getStackTraceAsString(th));
} }
// --------
// Helpers
// --------
/**
* Close all file handles.
*/
public static void close() { public static void close() {
if (fileWriter != null) { if (fileWriter != null) {
try { try {
@ -144,4 +159,26 @@ public final class ConsoleLogger {
} }
} }
} }
/**
* Write a message into the log file with a TimeStamp if enabled.
*
* @param message The message to write to the log
*/
private static void writeLog(String message) {
if (useLogging) {
String dateTime;
synchronized (DATE_FORMAT) {
dateTime = DATE_FORMAT.format(new Date());
}
try {
fileWriter.write(dateTime);
fileWriter.write(": ");
fileWriter.write(message);
fileWriter.write(NEW_LINE);
fileWriter.flush();
} catch (IOException ignored) {
}
}
}
} }

View File

@ -1,7 +1,7 @@
package fr.xephi.authme; package fr.xephi.authme;
import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.BackupSettings; import fr.xephi.authme.settings.properties.BackupSettings;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.StringUtils;
@ -30,7 +30,7 @@ public class PerformBackup {
private final String tblname; private final String tblname;
private final String path; private final String path;
private final File dataFolder; private final File dataFolder;
private final NewSetting settings; private final Settings settings;
/** /**
* Constructor for PerformBackup. * Constructor for PerformBackup.
@ -38,7 +38,7 @@ public class PerformBackup {
* @param instance AuthMe * @param instance AuthMe
* @param settings The plugin settings * @param settings The plugin settings
*/ */
public PerformBackup(AuthMe instance, NewSetting settings) { public PerformBackup(AuthMe instance, Settings settings) {
this.dataFolder = instance.getDataFolder(); this.dataFolder = instance.getDataFolder();
this.settings = settings; this.settings = settings;
this.dbName = settings.getProperty(DatabaseSettings.MYSQL_DATABASE); this.dbName = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
@ -60,7 +60,7 @@ public class PerformBackup {
if (!settings.getProperty(BackupSettings.ENABLED)) { if (!settings.getProperty(BackupSettings.ENABLED)) {
// Print a warning if the backup was requested via command or by another plugin // Print a warning if the backup was requested via command or by another plugin
if (cause == BackupCause.COMMAND || cause == BackupCause.OTHER) { if (cause == BackupCause.COMMAND || cause == BackupCause.OTHER) {
ConsoleLogger.showError("Can't perform a Backup: disabled in configuration. Cause of the Backup: " ConsoleLogger.warning("Can't perform a Backup: disabled in configuration. Cause of the Backup: "
+ cause.name()); + cause.name());
} }
return; return;
@ -76,7 +76,7 @@ public class PerformBackup {
if (doBackup()) { if (doBackup()) {
ConsoleLogger.info("A backup has been performed successfully. Cause of the Backup: " + cause.name()); ConsoleLogger.info("A backup has been performed successfully. Cause of the Backup: " + cause.name());
} else { } else {
ConsoleLogger.showError("Error while performing a backup! Cause of the Backup: " + cause.name()); ConsoleLogger.warning("Error while performing a backup! Cause of the Backup: " + cause.name());
} }
} }
@ -90,7 +90,7 @@ public class PerformBackup {
case SQLITE: case SQLITE:
return fileBackup(dbName + ".db"); return fileBackup(dbName + ".db");
default: default:
ConsoleLogger.showError("Unknown data source type '" + dataSourceType + "' for backup"); ConsoleLogger.warning("Unknown data source type '" + dataSourceType + "' for backup");
} }
return false; return false;
@ -113,7 +113,7 @@ public class PerformBackup {
ConsoleLogger.info("Backup created successfully."); ConsoleLogger.info("Backup created successfully.");
return true; return true;
} else { } else {
ConsoleLogger.showError("Could not create the backup! (Windows)"); ConsoleLogger.warning("Could not create the backup! (Windows)");
} }
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
ConsoleLogger.logException("Error during Windows backup:", e); ConsoleLogger.logException("Error during Windows backup:", e);
@ -128,7 +128,7 @@ public class PerformBackup {
ConsoleLogger.info("Backup created successfully."); ConsoleLogger.info("Backup created successfully.");
return true; return true;
} else { } else {
ConsoleLogger.showError("Could not create the backup!"); ConsoleLogger.warning("Could not create the backup!");
} }
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
ConsoleLogger.logException("Error during backup:", e); ConsoleLogger.logException("Error during backup:", e);
@ -147,8 +147,7 @@ public class PerformBackup {
copy("plugins" + File.separator + "AuthMe" + File.separator + backend, path + ".db"); copy("plugins" + File.separator + "AuthMe" + File.separator + backend, path + ".db");
return true; return true;
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError("Encountered an error during file backup: " + StringUtils.formatException(ex)); ConsoleLogger.logException("Encountered an error during file backup:", ex);
ConsoleLogger.writeStackTrace(ex);
} }
return false; return false;
} }
@ -166,7 +165,7 @@ public class PerformBackup {
if (new File(windowsPath + "\\bin\\mysqldump.exe").exists()) { if (new File(windowsPath + "\\bin\\mysqldump.exe").exists()) {
return true; return true;
} else { } else {
ConsoleLogger.showError("Mysql Windows Path is incorrect. Please check it"); ConsoleLogger.warning("Mysql Windows Path is incorrect. Please check it");
return false; return false;
} }
} }

View File

@ -4,10 +4,11 @@ import fr.xephi.authme.AuthMe;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.hooks.PluginHooks;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.ValidationService;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -27,19 +28,21 @@ public class API {
private static DataSource dataSource; private static DataSource dataSource;
private static PasswordSecurity passwordSecurity; private static PasswordSecurity passwordSecurity;
private static Management management; private static Management management;
private static PluginHooks pluginHooks;
private static ValidationService validationService;
/** /*
* Constructor for the deprecated API. * Constructor.
*
* @param instance AuthMe
*/ */
@Deprecated
@Inject @Inject
API(AuthMe instance, DataSource dataSource, PasswordSecurity passwordSecurity, Management management) { API(AuthMe instance, DataSource dataSource, PasswordSecurity passwordSecurity, Management management,
PluginHooks pluginHooks, ValidationService validationService) {
API.instance = instance; API.instance = instance;
API.dataSource = dataSource; API.dataSource = dataSource;
API.passwordSecurity = passwordSecurity; API.passwordSecurity = passwordSecurity;
API.management = management; API.management = management;
API.pluginHooks = pluginHooks;
API.validationService = validationService;
} }
/** /**
@ -47,7 +50,6 @@ public class API {
* *
* @return AuthMe instance * @return AuthMe instance
*/ */
@Deprecated
public static AuthMe hookAuthMe() { public static AuthMe hookAuthMe() {
if (instance != null) { if (instance != null) {
return instance; return instance;
@ -66,7 +68,6 @@ public class API {
* @param player The player to verify * @param player The player to verify
* @return true if the player is authenticated * @return true if the player is authenticated
*/ */
@Deprecated
public static boolean isAuthenticated(Player player) { public static boolean isAuthenticated(Player player) {
return PlayerCache.getInstance().isAuthenticated(player.getName()); return PlayerCache.getInstance().isAuthenticated(player.getName());
} }
@ -77,12 +78,10 @@ public class API {
* @param player The player to verify * @param player The player to verify
* @return true if the player is unrestricted * @return true if the player is unrestricted
*/ */
@Deprecated
public static boolean isUnrestricted(Player player) { public static boolean isUnrestricted(Player player) {
return Utils.isUnrestricted(player); return validationService.isUnrestricted(player.getName());
} }
@Deprecated
public static Location getLastLocation(Player player) { public static Location getLastLocation(Player player) {
try { try {
PlayerAuth auth = PlayerCache.getInstance().getAuth(player.getName().toLowerCase()); PlayerAuth auth = PlayerCache.getInstance().getAuth(player.getName().toLowerCase());
@ -99,7 +98,6 @@ public class API {
} }
} }
@Deprecated
public static void setPlayerInventory(Player player, ItemStack[] content, public static void setPlayerInventory(Player player, ItemStack[] content,
ItemStack[] armor) { ItemStack[] armor) {
try { try {
@ -115,7 +113,6 @@ public class API {
* @param playerName The player name to verify * @param playerName The player name to verify
* @return true if player is registered * @return true if player is registered
*/ */
@Deprecated
public static boolean isRegistered(String playerName) { public static boolean isRegistered(String playerName) {
String player = playerName.toLowerCase(); String player = playerName.toLowerCase();
return dataSource.isAuthAvailable(player); return dataSource.isAuthAvailable(player);
@ -128,7 +125,6 @@ public class API {
* @param passwordToCheck The password to check * @param passwordToCheck The password to check
* @return true if the password is correct, false otherwise * @return true if the password is correct, false otherwise
*/ */
@Deprecated
public static boolean checkPassword(String playerName, String passwordToCheck) { public static boolean checkPassword(String playerName, String passwordToCheck) {
return isRegistered(playerName) && passwordSecurity.comparePassword(passwordToCheck, playerName); return isRegistered(playerName) && passwordSecurity.comparePassword(passwordToCheck, playerName);
} }
@ -140,7 +136,6 @@ public class API {
* @param password The password * @param password The password
* @return true if the player was registered correctly * @return true if the player was registered correctly
*/ */
@Deprecated
public static boolean registerPlayer(String playerName, String password) { public static boolean registerPlayer(String playerName, String password) {
String name = playerName.toLowerCase(); String name = playerName.toLowerCase();
HashedPassword hashedPassword = passwordSecurity.computeHash(password, name); HashedPassword hashedPassword = passwordSecurity.computeHash(password, name);
@ -161,12 +156,10 @@ public class API {
* *
* @param player The player to log in * @param player The player to log in
*/ */
@Deprecated
public static void forceLogin(Player player) { public static void forceLogin(Player player) {
management.performLogin(player, "dontneed", true); management.performLogin(player, "dontneed", true);
} }
@Deprecated
public AuthMe getPlugin() { public AuthMe getPlugin() {
return instance; return instance;
} }
@ -177,9 +170,8 @@ public class API {
* @param player The player to verify * @param player The player to verify
* @return true if player is an npc * @return true if player is an npc
*/ */
@Deprecated
public boolean isNPC(Player player) { public boolean isNPC(Player player) {
return instance.getPluginHooks().isNpc(player); return pluginHooks.isNpc(player);
} }
} }

View File

@ -3,12 +3,15 @@ package fr.xephi.authme.api;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.hooks.PluginHooks;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.ValidationService;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import javax.inject.Inject; import javax.inject.Inject;
@ -22,34 +25,41 @@ public class NewAPI {
public static NewAPI singleton; public static NewAPI singleton;
public final AuthMe plugin; public final AuthMe plugin;
private final PluginHooks pluginHooks;
private final DataSource dataSource;
private final PasswordSecurity passwordSecurity;
private final Management management;
private final ValidationService validationService;
private final PlayerCache playerCache;
/** /*
* Constructor for NewAPI. * Constructor for NewAPI.
*
* @param plugin The AuthMe plugin instance
*/ */
@Inject @Inject
public NewAPI(AuthMe plugin) { NewAPI(AuthMe plugin, PluginHooks pluginHooks, DataSource dataSource, PasswordSecurity passwordSecurity,
Management management, ValidationService validationService, PlayerCache playerCache) {
this.plugin = plugin; this.plugin = plugin;
this.pluginHooks = pluginHooks;
this.dataSource = dataSource;
this.passwordSecurity = passwordSecurity;
this.management = management;
this.validationService = validationService;
this.playerCache = playerCache;
NewAPI.singleton = this;
} }
/** /**
* Get the API object for AuthMe. * Get the API object for AuthMe.
* *
* @return The API object, or null if the AuthMe plugin instance could not be retrieved * @return The API object, or null if the AuthMe plugin is not enabled or not fully initialized yet
* from the server environment
*/ */
public static NewAPI getInstance() { public static NewAPI getInstance() {
if (singleton != null) { if (singleton != null) {
return singleton; return singleton;
} }
Plugin p = Bukkit.getServer().getPluginManager().getPlugin("AuthMe"); // NewAPI is initialized in AuthMe#onEnable -> if singleton is null,
if (p == null || !(p instanceof AuthMe)) { // it means AuthMe isn't initialized (yet)
return null; return null;
}
AuthMe authme = (AuthMe) p;
singleton = new NewAPI(authme);
return singleton;
} }
/** /**
@ -78,7 +88,7 @@ public class NewAPI {
* @return true if the player is authenticated * @return true if the player is authenticated
*/ */
public boolean isAuthenticated(Player player) { public boolean isAuthenticated(Player player) {
return PlayerCache.getInstance().isAuthenticated(player.getName()); return playerCache.isAuthenticated(player.getName());
} }
/** /**
@ -88,7 +98,7 @@ public class NewAPI {
* @return true if the player is an npc * @return true if the player is an npc
*/ */
public boolean isNPC(Player player) { public boolean isNPC(Player player) {
return plugin.getPluginHooks().isNpc(player); return pluginHooks.isNpc(player);
} }
/** /**
@ -100,17 +110,17 @@ public class NewAPI {
* @see fr.xephi.authme.settings.properties.RestrictionSettings#UNRESTRICTED_NAMES * @see fr.xephi.authme.settings.properties.RestrictionSettings#UNRESTRICTED_NAMES
*/ */
public boolean isUnrestricted(Player player) { public boolean isUnrestricted(Player player) {
return Utils.isUnrestricted(player); return validationService.isUnrestricted(player.getName());
} }
/** /**
* Get the last location of a player. * Get the last location of an online player.
* *
* @param player The player to process * @param player The player to process
* @return Location The location of the player * @return Location The location of the player
*/ */
public Location getLastLocation(Player player) { public Location getLastLocation(Player player) {
PlayerAuth auth = PlayerCache.getInstance().getAuth(player.getName()); PlayerAuth auth = playerCache.getAuth(player.getName());
if (auth != null) { if (auth != null) {
return new Location(Bukkit.getWorld(auth.getWorld()), auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ()); return new Location(Bukkit.getWorld(auth.getWorld()), auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ());
} }
@ -125,7 +135,7 @@ public class NewAPI {
*/ */
public boolean isRegistered(String playerName) { public boolean isRegistered(String playerName) {
String player = playerName.toLowerCase(); String player = playerName.toLowerCase();
return plugin.getDataSource().isAuthAvailable(player); return dataSource.isAuthAvailable(player);
} }
/** /**
@ -136,7 +146,7 @@ public class NewAPI {
* @return true if the password is correct, false otherwise * @return true if the password is correct, false otherwise
*/ */
public boolean checkPassword(String playerName, String passwordToCheck) { public boolean checkPassword(String playerName, String passwordToCheck) {
return isRegistered(playerName) && plugin.getPasswordSecurity().comparePassword(passwordToCheck, playerName); return passwordSecurity.comparePassword(passwordToCheck, playerName);
} }
/** /**
@ -149,7 +159,7 @@ public class NewAPI {
*/ */
public boolean registerPlayer(String playerName, String password) { public boolean registerPlayer(String playerName, String password) {
String name = playerName.toLowerCase(); String name = playerName.toLowerCase();
HashedPassword result = plugin.getPasswordSecurity().computeHash(password, name); HashedPassword result = passwordSecurity.computeHash(password, name);
if (isRegistered(name)) { if (isRegistered(name)) {
return false; return false;
} }
@ -158,7 +168,7 @@ public class NewAPI {
.password(result) .password(result)
.realName(playerName) .realName(playerName)
.build(); .build();
return plugin.getDataSource().saveAuth(auth); return dataSource.saveAuth(auth);
} }
/** /**
@ -167,7 +177,7 @@ public class NewAPI {
* @param player The player to log in * @param player The player to log in
*/ */
public void forceLogin(Player player) { public void forceLogin(Player player) {
plugin.getManagement().performLogin(player, "dontneed", true); management.performLogin(player, "dontneed", true);
} }
/** /**
@ -176,7 +186,7 @@ public class NewAPI {
* @param player The player to log out * @param player The player to log out
*/ */
public void forceLogout(Player player) { public void forceLogout(Player player) {
plugin.getManagement().performLogout(player); management.performLogout(player);
} }
/** /**
@ -187,7 +197,7 @@ public class NewAPI {
* @param autoLogin Should the player be authenticated automatically after the registration? * @param autoLogin Should the player be authenticated automatically after the registration?
*/ */
public void forceRegister(Player player, String password, boolean autoLogin) { public void forceRegister(Player player, String password, boolean autoLogin) {
plugin.getManagement().performRegister(player, password, null, autoLogin); management.performRegister(player, password, null, autoLogin);
} }
/** /**
@ -206,6 +216,6 @@ public class NewAPI {
* @param player The player to unregister * @param player The player to unregister
*/ */
public void forceUnregister(Player player) { public void forceUnregister(Player player) {
plugin.getManagement().performUnregister(player, "", true); management.performUnregisterByAdmin(null, player.getName(), player);
} }
} }

View File

@ -2,7 +2,7 @@ package fr.xephi.authme.cache;
import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.security.RandomString; import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.settings.properties.SecuritySettings;
import javax.inject.Inject; import javax.inject.Inject;
@ -21,10 +21,10 @@ public class CaptchaManager implements SettingsDependent {
private int captchaLength; private int captchaLength;
@Inject @Inject
CaptchaManager(NewSetting settings) { CaptchaManager(Settings settings) {
this.playerCounts = new ConcurrentHashMap<>(); this.playerCounts = new ConcurrentHashMap<>();
this.captchaCodes = new ConcurrentHashMap<>(); this.captchaCodes = new ConcurrentHashMap<>();
loadSettings(settings); reload(settings);
} }
/** /**
@ -123,7 +123,7 @@ public class CaptchaManager implements SettingsDependent {
} }
@Override @Override
public void loadSettings(NewSetting settings) { public void reload(Settings settings) {
this.isEnabled = settings.getProperty(SecuritySettings.USE_CAPTCHA); this.isEnabled = settings.getProperty(SecuritySettings.USE_CAPTCHA);
this.threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TRIES_BEFORE_CAPTCHA); this.threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TRIES_BEFORE_CAPTCHA);
this.captchaLength = settings.getProperty(SecuritySettings.CAPTCHA_LENGTH); this.captchaLength = settings.getProperty(SecuritySettings.CAPTCHA_LENGTH);

View File

@ -0,0 +1,96 @@
package fr.xephi.authme.cache;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.initialization.HasCleanup;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.PluginSettings;
import javax.inject.Inject;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Manages sessions, allowing players to be automatically logged in if they join again
* within a configurable amount of time.
*/
public class SessionManager implements SettingsDependent, HasCleanup {
private static final int MINUTE_IN_MILLIS = 60_000;
// Player -> expiration of session in milliseconds
private final Map<String, Long> sessions = new ConcurrentHashMap<>();
private boolean enabled;
private int timeoutInMinutes;
@Inject
SessionManager(Settings settings) {
reload(settings);
}
/**
* Check if a session is available for the given player.
*
* @param name The name to check.
* @return True if a session is found.
*/
public boolean hasSession(String name) {
if (enabled) {
Long timeout = sessions.get(name.toLowerCase());
if (timeout != null) {
return System.currentTimeMillis() <= timeout;
}
}
return false;
}
/**
* Add a player session to the cache.
*
* @param name The name of the player.
*/
public void addSession(String name) {
if (enabled) {
long timeout = System.currentTimeMillis() + timeoutInMinutes * MINUTE_IN_MILLIS;
sessions.put(name.toLowerCase(), timeout);
}
}
/**
* Remove a player's session from the cache.
*
* @param name The name of the player.
*/
public void removeSession(String name) {
this.sessions.remove(name.toLowerCase());
}
@Override
public void reload(Settings settings) {
timeoutInMinutes = settings.getProperty(PluginSettings.SESSIONS_TIMEOUT);
boolean oldEnabled = enabled;
enabled = timeoutInMinutes > 0 && settings.getProperty(PluginSettings.SESSIONS_ENABLED);
// With this reload, the sessions feature has just been disabled, so clear all stored sessions
if (oldEnabled && !enabled) {
sessions.clear();
ConsoleLogger.fine("Sessions disabled: cleared all sessions");
}
}
@Override
public void performCleanup() {
if (!enabled) {
return;
}
final long currentTime = System.currentTimeMillis();
Iterator<Map.Entry<String, Long>> iterator = sessions.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Long> entry = iterator.next();
if (entry.getValue() < currentTime) {
iterator.remove();
}
}
}
}

View File

@ -3,7 +3,7 @@ package fr.xephi.authme.cache;
import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages; import fr.xephi.authme.output.Messages;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils;
@ -30,11 +30,11 @@ public class TempbanManager implements SettingsDependent {
private int length; private int length;
@Inject @Inject
TempbanManager(BukkitService bukkitService, Messages messages, NewSetting settings) { TempbanManager(BukkitService bukkitService, Messages messages, Settings settings) {
this.ipLoginFailureCounts = new ConcurrentHashMap<>(); this.ipLoginFailureCounts = new ConcurrentHashMap<>();
this.bukkitService = bukkitService; this.bukkitService = bukkitService;
this.messages = messages; this.messages = messages;
loadSettings(settings); reload(settings);
} }
/** /**
@ -108,7 +108,7 @@ public class TempbanManager implements SettingsDependent {
} }
@Override @Override
public void loadSettings(NewSetting settings) { public void reload(Settings settings) {
this.isEnabled = settings.getProperty(SecuritySettings.TEMPBAN_ON_MAX_LOGINS); this.isEnabled = settings.getProperty(SecuritySettings.TEMPBAN_ON_MAX_LOGINS);
this.threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TEMPBAN); this.threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TEMPBAN);
this.length = settings.getProperty(SecuritySettings.TEMPBAN_LENGTH); this.length = settings.getProperty(SecuritySettings.TEMPBAN_LENGTH);

View File

@ -85,6 +85,13 @@ public class PlayerAuth {
return groupId; return groupId;
} }
public void setQuitLocation(Location location) {
x = location.getBlockX();
y = location.getBlockY();
z = location.getBlockZ();
world = location.getWorld().getName();
}
public double getQuitLocX() { public double getQuitLocX() {
return x; return x;
} }

View File

@ -1,10 +1,10 @@
package fr.xephi.authme.cache.auth; package fr.xephi.authme.cache.auth;
import fr.xephi.authme.ConsoleLogger;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
* Used to manage player's Authenticated status
*/ */
public class PlayerCache { public class PlayerCache {
@ -34,7 +34,6 @@ public class PlayerCache {
* @param auth PlayerAuth * @param auth PlayerAuth
*/ */
public void addPlayer(PlayerAuth auth) { public void addPlayer(PlayerAuth auth) {
ConsoleLogger.debug("ADDED PLAYER TO CACHE " + auth.getNickname());
cache.put(auth.getNickname().toLowerCase(), auth); cache.put(auth.getNickname().toLowerCase(), auth);
} }
@ -44,7 +43,6 @@ public class PlayerCache {
* @param auth PlayerAuth * @param auth PlayerAuth
*/ */
public void updatePlayer(PlayerAuth auth) { public void updatePlayer(PlayerAuth auth) {
ConsoleLogger.debug("UPDATE PLAYER " + auth.getNickname());
cache.put(auth.getNickname(), auth); cache.put(auth.getNickname(), auth);
} }
@ -54,16 +52,15 @@ public class PlayerCache {
* @param user String * @param user String
*/ */
public void removePlayer(String user) { public void removePlayer(String user) {
ConsoleLogger.debug("REMOVE PLAYER " + user);
cache.remove(user.toLowerCase()); cache.remove(user.toLowerCase());
} }
/** /**
* Method isAuthenticated. * get player's authenticated status.
* *
* @param user String * @param user player's name
* *
* @return boolean * @return true if player is logged in, false otherwise.
*/ */
public boolean isAuthenticated(String user) { public boolean isAuthenticated(String user) {
return cache.containsKey(user.toLowerCase()); return cache.containsKey(user.toLowerCase());

View File

@ -1,131 +0,0 @@
package fr.xephi.authme.cache.backup;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import org.bukkit.entity.Player;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
public class JsonCache {
private final Gson gson;
private final File cacheDir;
public JsonCache() {
cacheDir = new File(AuthMe.getInstance().getDataFolder(), "cache");
if (!cacheDir.exists() && !cacheDir.isDirectory() && !cacheDir.mkdir()) {
ConsoleLogger.showError("Failed to create cache directory.");
}
gson = new GsonBuilder()
.registerTypeAdapter(PlayerData.class, new PlayerDataSerializer())
.registerTypeAdapter(PlayerData.class, new PlayerDataDeserializer())
.setPrettyPrinting()
.create();
}
public PlayerData readCache(Player player) {
String name = player.getName().toLowerCase();
File file = new File(cacheDir, name + File.separator + "cache.json");
if (!file.exists()) {
return null;
}
try {
String str = Files.toString(file, Charsets.UTF_8);
return gson.fromJson(str, PlayerData.class);
} catch (IOException e) {
ConsoleLogger.writeStackTrace(e);
return null;
}
}
public void removeCache(Player player) {
String name = player.getName().toLowerCase();
File file = new File(cacheDir, name);
if (file.exists()) {
purgeDirectory(file);
if (!file.delete()) {
ConsoleLogger.showError("Failed to remove" + player.getName() + "cache.");
}
}
}
public boolean doesCacheExist(Player player) {
String name = player.getName().toLowerCase();
File file = new File(cacheDir, name + File.separator + "cache.json");
return file.exists();
}
private class PlayerDataDeserializer implements JsonDeserializer<PlayerData> {
@Override
public PlayerData deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext context) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject == null) {
return null;
}
String group = null;
boolean operator = false;
boolean fly = false;
JsonElement e;
if ((e = jsonObject.get("group")) != null) {
group = e.getAsString();
}
if ((e = jsonObject.get("operator")) != null) {
operator = e.getAsBoolean();
}
if ((e = jsonObject.get("fly")) != null) {
fly = e.getAsBoolean();
}
return new PlayerData(group, operator, fly);
}
}
private class PlayerDataSerializer implements JsonSerializer<PlayerData> {
@Override
public JsonElement serialize(PlayerData playerData, Type type,
JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("group", playerData.getGroup());
jsonObject.addProperty("operator", playerData.getOperator());
jsonObject.addProperty("fly", playerData.isFlyEnabled());
return jsonObject;
}
}
/**
* Delete a given directory and all its content.
*
* @param directory The directory to remove
*/
private static void purgeDirectory(File directory) {
if (!directory.isDirectory()) {
return;
}
File[] files = directory.listFiles();
if (files == null) {
return;
}
for (File target : files) {
if (target.isDirectory()) {
purgeDirectory(target);
}
target.delete();
}
}
}

View File

@ -1,26 +0,0 @@
package fr.xephi.authme.cache.backup;
public class PlayerData {
private final String group;
private final boolean operator;
private final boolean flyEnabled;
public PlayerData(String group, boolean operator, boolean flyEnabled) {
this.group = group;
this.operator = operator;
this.flyEnabled = flyEnabled;
}
public String getGroup() {
return group;
}
public boolean getOperator() {
return operator;
}
public boolean isFlyEnabled() {
return flyEnabled;
}
}

View File

@ -0,0 +1,214 @@
package fr.xephi.authme.cache.backup;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.limbo.PlayerData;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.FileUtils;
import fr.xephi.authme.util.Utils;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
/**
* Class used to store player's data (OP, flying, speed, position) to disk.
*/
public class PlayerDataStorage {
private final Gson gson;
private final File cacheDir;
private PermissionsManager permissionsManager;
private SpawnLoader spawnLoader;
private BukkitService bukkitService;
@Inject
PlayerDataStorage(@DataFolder File dataFolder, PermissionsManager permsMan,
SpawnLoader spawnLoader, BukkitService bukkitService) {
this.permissionsManager = permsMan;
this.spawnLoader = spawnLoader;
this.bukkitService = bukkitService;
cacheDir = new File(dataFolder, "playerdata");
if (!cacheDir.exists() && !cacheDir.isDirectory() && !cacheDir.mkdir()) {
ConsoleLogger.warning("Failed to create userdata directory.");
}
gson = new GsonBuilder()
.registerTypeAdapter(PlayerData.class, new PlayerDataSerializer())
.registerTypeAdapter(PlayerData.class, new PlayerDataDeserializer())
.setPrettyPrinting()
.create();
}
/**
* Read and construct new PlayerData from existing player data.
*
* @param player player to read
*
* @return PlayerData object if the data is exist, null otherwise.
*/
public PlayerData readData(Player player) {
String id = Utils.getUUIDorName(player);
File file = new File(cacheDir, id + File.separator + "data.json");
if (!file.exists()) {
return null;
}
try {
String str = Files.toString(file, Charsets.UTF_8);
return gson.fromJson(str, PlayerData.class);
} catch (IOException e) {
ConsoleLogger.logException("Could not read player data on disk for '" + player.getName() + "'", e);
return null;
}
}
/**
* Save player data (OP, flying, location, etc) to disk.
*
* @param player player to save
*/
public void saveData(Player player) {
String id = Utils.getUUIDorName(player);
Location location = spawnLoader.getPlayerLocationOrSpawn(player);
String group = "";
if (permissionsManager.hasGroupSupport()) {
group = permissionsManager.getPrimaryGroup(player);
}
boolean operator = player.isOp();
boolean canFly = player.getAllowFlight();
float walkSpeed = player.getWalkSpeed();
float flySpeed = player.getFlySpeed();
PlayerData playerData = new PlayerData(location, operator, group, canFly, walkSpeed, flySpeed);
try {
File file = new File(cacheDir, id + File.separator + "data.json");
Files.createParentDirs(file);
Files.touch(file);
Files.write(gson.toJson(playerData), file, Charsets.UTF_8);
} catch (IOException e) {
ConsoleLogger.logException("Failed to write " + player.getName() + " data.", e);
}
}
/**
* Remove player data, this will delete
* "playerdata/&lt;uuid or name&gt;/" folder from disk.
*
* @param player player to remove
*/
public void removeData(Player player) {
String id = Utils.getUUIDorName(player);
File file = new File(cacheDir, id);
if (file.exists()) {
FileUtils.purgeDirectory(file);
if (!file.delete()) {
ConsoleLogger.warning("Failed to remove " + player.getName() + " cache.");
}
}
}
/**
* Use to check is player data is exist.
*
* @param player player to check
*
* @return true if data exist, false otherwise.
*/
public boolean hasData(Player player) {
String id = Utils.getUUIDorName(player);
File file = new File(cacheDir, id + File.separator + "data.json");
return file.exists();
}
private class PlayerDataDeserializer implements JsonDeserializer<PlayerData> {
@Override
public PlayerData deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext context) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject == null) {
return null;
}
Location loc = null;
String group = "";
boolean operator = false;
boolean canFly = false;
float walkSpeed = 0.2f;
float flySpeed = 0.2f;
JsonElement e;
if ((e = jsonObject.getAsJsonObject("location")) != null) {
JsonObject obj = e.getAsJsonObject();
World world = bukkitService.getWorld(obj.get("world").getAsString());
if (world != null) {
double x = obj.get("x").getAsDouble();
double y = obj.get("y").getAsDouble();
double z = obj.get("z").getAsDouble();
float yaw = obj.get("yaw").getAsFloat();
float pitch = obj.get("pitch").getAsFloat();
loc = new Location(world, x, y, z, yaw, pitch);
}
}
if ((e = jsonObject.get("group")) != null) {
group = e.getAsString();
}
if ((e = jsonObject.get("operator")) != null) {
operator = e.getAsBoolean();
}
if ((e = jsonObject.get("can-fly")) != null) {
canFly = e.getAsBoolean();
}
if ((e = jsonObject.get("walk-speed")) != null) {
walkSpeed = e.getAsFloat();
}
if ((e = jsonObject.get("fly-speed")) != null) {
flySpeed = e.getAsFloat();
}
return new PlayerData(loc, operator, group, canFly, walkSpeed, flySpeed);
}
}
private class PlayerDataSerializer implements JsonSerializer<PlayerData> {
@Override
public JsonElement serialize(PlayerData playerData, Type type,
JsonSerializationContext context) {
JsonObject obj = new JsonObject();
obj.addProperty("group", playerData.getGroup());
Location loc = playerData.getLocation();
JsonObject obj2 = new JsonObject();
obj2.addProperty("world", loc.getWorld().getName());
obj2.addProperty("x", loc.getX());
obj2.addProperty("y", loc.getY());
obj2.addProperty("z", loc.getZ());
obj2.addProperty("yaw", loc.getYaw());
obj2.addProperty("pitch", loc.getPitch());
obj.add("location", obj2);
obj.addProperty("operator", playerData.isOperator());
obj.addProperty("can-fly", playerData.isCanFly());
obj.addProperty("walk-speed", playerData.getWalkSpeed());
obj.addProperty("fly-speed", playerData.getFlySpeed());
return obj;
}
}
}

View File

@ -1,111 +1,164 @@
package fr.xephi.authme.cache.limbo; package fr.xephi.authme.cache.limbo;
import fr.xephi.authme.cache.backup.JsonCache; import fr.xephi.authme.cache.backup.PlayerDataStorage;
import fr.xephi.authme.cache.backup.PlayerData;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
/** /**
* Manages all {@link LimboPlayer} instances. * Manages all {@link PlayerData} instances.
*/ */
public class LimboCache { public class LimboCache {
private final ConcurrentHashMap<String, LimboPlayer> cache = new ConcurrentHashMap<>(); private final Map<String, PlayerData> cache = new ConcurrentHashMap<>();
private final JsonCache jsonCache = new JsonCache();
@Inject private PlayerDataStorage playerDataStorage;
private Settings settings;
private PermissionsManager permissionsManager; private PermissionsManager permissionsManager;
@Inject
private SpawnLoader spawnLoader; private SpawnLoader spawnLoader;
@Inject @Inject
LimboCache(PermissionsManager permissionsManager, SpawnLoader spawnLoader) { LimboCache(Settings settings, PermissionsManager permissionsManager,
SpawnLoader spawnLoader, PlayerDataStorage playerDataStorage) {
this.settings = settings;
this.permissionsManager = permissionsManager; this.permissionsManager = permissionsManager;
this.spawnLoader = spawnLoader; this.spawnLoader = spawnLoader;
this.playerDataStorage = playerDataStorage;
} }
/** /**
* Add a limbo player. * Load player data if exist, otherwise current player's data will be stored.
* *
* @param player Player instance to add. * @param player Player instance to add.
*/ */
public void addLimboPlayer(Player player) { public void addPlayerData(Player player) {
String name = player.getName().toLowerCase(); String name = player.getName().toLowerCase();
Location location = player.isDead() ? spawnLoader.getSpawnLocation(player) : player.getLocation(); Location location = spawnLoader.getPlayerLocationOrSpawn(player);
boolean operator = player.isOp(); boolean operator = player.isOp();
boolean flyEnabled = player.getAllowFlight(); boolean flyEnabled = player.getAllowFlight();
float walkSpeed = player.getWalkSpeed();
float flySpeed = player.getFlySpeed();
String playerGroup = ""; String playerGroup = "";
if (permissionsManager.hasGroupSupport()) { if (permissionsManager.hasGroupSupport()) {
playerGroup = permissionsManager.getPrimaryGroup(player); playerGroup = permissionsManager.getPrimaryGroup(player);
} }
if (jsonCache.doesCacheExist(player)) { if (playerDataStorage.hasData(player)) {
PlayerData cache = jsonCache.readCache(player); PlayerData cache = playerDataStorage.readData(player);
if (cache != null) { if (cache != null) {
location = cache.getLocation();
playerGroup = cache.getGroup(); playerGroup = cache.getGroup();
operator = cache.getOperator(); operator = cache.isOperator();
flyEnabled = cache.isFlyEnabled(); flyEnabled = cache.isCanFly();
walkSpeed = cache.getWalkSpeed();
flySpeed = cache.getFlySpeed();
} }
} else {
playerDataStorage.saveData(player);
} }
cache.put(name, new PlayerData(location, operator, playerGroup, flyEnabled, walkSpeed, flySpeed));
cache.put(name, new LimboPlayer(name, location, operator, playerGroup, flyEnabled));
} }
/** /**
* Method deleteLimboPlayer. * Restore player's data to player if exist.
* *
* @param name String * @param player Player instance to restore
*/ */
public void deleteLimboPlayer(String name) { public void restoreData(Player player) {
checkNotNull(name); String lowerName = player.getName().toLowerCase();
name = name.toLowerCase(); if (cache.containsKey(lowerName)) {
LimboPlayer cachedPlayer = cache.remove(name); PlayerData data = cache.get(lowerName);
player.setOp(data.isOperator());
player.setAllowFlight(data.isCanFly());
float walkSpeed = data.getWalkSpeed();
float flySpeed = data.getFlySpeed();
// Reset the speed value if it was 0
if(walkSpeed == 0f) {
walkSpeed = 0.2f;
}
if(flySpeed == 0f) {
flySpeed = 0.2f;
}
player.setWalkSpeed(walkSpeed);
player.setFlySpeed(flySpeed);
restoreGroup(player, data.getGroup());
data.clearTasks();
}
}
/**
* Remove PlayerData from cache and disk.
*
* @param player Player player to remove.
*/
public void deletePlayerData(Player player) {
removeFromCache(player);
playerDataStorage.removeData(player);
}
/**
* Remove PlayerData from cache.
*
* @param player player to remove.
*/
public void removeFromCache(Player player) {
String name = player.getName().toLowerCase();
PlayerData cachedPlayer = cache.remove(name);
if (cachedPlayer != null) { if (cachedPlayer != null) {
cachedPlayer.clearTasks(); cachedPlayer.clearTasks();
} }
} }
/** /**
* Method getLimboPlayer. * Method getPlayerData.
* *
* @param name String * @param name String
* *
* @return LimboPlayer * @return PlayerData
*/ */
public LimboPlayer getLimboPlayer(String name) { public PlayerData getPlayerData(String name) {
checkNotNull(name); checkNotNull(name);
return cache.get(name.toLowerCase()); return cache.get(name.toLowerCase());
} }
/** /**
* Method hasLimboPlayer. * Method hasPlayerData.
* *
* @param name String * @param name String
* *
* @return boolean * @return boolean
*/ */
public boolean hasLimboPlayer(String name) { public boolean hasPlayerData(String name) {
checkNotNull(name); checkNotNull(name);
return cache.containsKey(name.toLowerCase()); return cache.containsKey(name.toLowerCase());
} }
/** /**
* Method updateLimboPlayer. * Method updatePlayerData.
* *
* @param player Player * @param player Player
*/ */
public void updateLimboPlayer(Player player) { public void updatePlayerData(Player player) {
checkNotNull(player); checkNotNull(player);
deleteLimboPlayer(player.getName().toLowerCase()); removeFromCache(player);
addLimboPlayer(player); addPlayerData(player);
} }
private void restoreGroup(Player player, String group) {
if (!StringUtils.isEmpty(group) && permissionsManager.hasGroupSupport()
&& settings.getProperty(PluginSettings.ENABLE_PERMISSION_CHECK)) {
permissionsManager.setGroup(player, group);
}
}
} }

View File

@ -7,32 +7,25 @@ import org.bukkit.scheduler.BukkitTask;
* Represents a player which is not logged in and keeps track of certain states (like OP status, flying) * Represents a player which is not logged in and keeps track of certain states (like OP status, flying)
* which may be revoked from the player until he has logged in or registered. * which may be revoked from the player until he has logged in or registered.
*/ */
public class LimboPlayer { public class PlayerData {
private final String name; private final boolean canFly;
private final boolean fly; private final boolean operator;
private Location loc = null; private final String group;
private final Location loc;
private final float walkSpeed;
private final float flySpeed;
private BukkitTask timeoutTask = null; private BukkitTask timeoutTask = null;
private BukkitTask messageTask = null; private BukkitTask messageTask = null;
private boolean operator = false;
private String group;
public LimboPlayer(String name, Location loc, boolean operator, public PlayerData(Location loc, boolean operator,
String group, boolean fly) { String group, boolean fly, float walkSpeed, float flySpeed) {
this.name = name;
this.loc = loc; this.loc = loc;
this.operator = operator; this.operator = operator;
this.group = group; this.group = group;
this.fly = fly; this.canFly = fly;
} this.walkSpeed = walkSpeed;
this.flySpeed = flySpeed;
/**
* Return the name of the player.
*
* @return The player's name
*/
public String getName() {
return name;
} }
/** /**
@ -40,7 +33,7 @@ public class LimboPlayer {
* *
* @return The player's location * @return The player's location
*/ */
public Location getLoc() { public Location getLocation() {
return loc; return loc;
} }
@ -62,8 +55,16 @@ public class LimboPlayer {
return group; return group;
} }
public boolean isFly() { public boolean isCanFly() {
return fly; return canFly;
}
public float getWalkSpeed() {
return walkSpeed;
}
public float getFlySpeed() {
return flySpeed;
} }
/** /**

View File

@ -1,8 +1,8 @@
package fr.xephi.authme.command; package fr.xephi.authme.command;
import ch.jalu.injector.Injector;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.command.help.HelpProvider; import fr.xephi.authme.command.help.HelpProvider;
import fr.xephi.authme.initialization.AuthMeServiceInitializer;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.StringUtils;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
@ -37,12 +37,12 @@ public class CommandHandler {
private Map<Class<? extends ExecutableCommand>, ExecutableCommand> commands = new HashMap<>(); private Map<Class<? extends ExecutableCommand>, ExecutableCommand> commands = new HashMap<>();
@Inject @Inject
public CommandHandler(AuthMeServiceInitializer initializer, CommandMapper commandMapper, public CommandHandler(Injector injector, CommandMapper commandMapper,
PermissionsManager permissionsManager, HelpProvider helpProvider) { PermissionsManager permissionsManager, HelpProvider helpProvider) {
this.commandMapper = commandMapper; this.commandMapper = commandMapper;
this.permissionsManager = permissionsManager; this.permissionsManager = permissionsManager;
this.helpProvider = helpProvider; this.helpProvider = helpProvider;
initializeCommands(initializer, commandMapper.getCommandClasses()); initializeCommands(injector, commandMapper.getCommandClasses());
} }
/** /**
@ -90,12 +90,13 @@ public class CommandHandler {
/** /**
* Initialize all required ExecutableCommand objects. * Initialize all required ExecutableCommand objects.
* *
* @param injector the injector
* @param commandClasses the classes to instantiate * @param commandClasses the classes to instantiate
*/ */
private void initializeCommands(AuthMeServiceInitializer initializer, private void initializeCommands(Injector injector,
Set<Class<? extends ExecutableCommand>> commandClasses) { Set<Class<? extends ExecutableCommand>> commandClasses) {
for (Class<? extends ExecutableCommand> clazz : commandClasses) { for (Class<? extends ExecutableCommand> clazz : commandClasses) {
commands.put(clazz, initializer.newInstance(clazz)); commands.put(clazz, injector.newInstance(clazz));
} }
} }

View File

@ -2,7 +2,7 @@ package fr.xephi.authme.command;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages; import fr.xephi.authme.output.Messages;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.util.ValidationService; import fr.xephi.authme.util.ValidationService;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -18,7 +18,7 @@ public class CommandService {
@Inject @Inject
private Messages messages; private Messages messages;
@Inject @Inject
private NewSetting settings; private Settings settings;
@Inject @Inject
private ValidationService validationService; private ValidationService validationService;
@ -53,6 +53,16 @@ public class CommandService {
return messages.retrieve(key); return messages.retrieve(key);
} }
/**
* Retrieve a message as a single String by its message key.
*
* @param key The message to retrieve
* @return The message
*/
public String retrieveSingle(MessageKey key) {
return messages.retrieveSingle(key);
}
/** /**
* Retrieve the given property's value. * Retrieve the given property's value.
* *
@ -69,7 +79,7 @@ public class CommandService {
* *
* @return The settings manager * @return The settings manager
*/ */
public NewSetting getSettings() { public Settings getSettings() {
return settings; return settings;
} }

View File

@ -75,7 +75,7 @@ public class ChangePasswordAdminCommand implements ExecutableCommand {
if (dataSource.updatePassword(auth)) { if (dataSource.updatePassword(auth)) {
commandService.send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS); commandService.send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS);
ConsoleLogger.info(playerNameLowerCase + "'s password changed"); ConsoleLogger.info(sender.getName() + " changed password of " + playerNameLowerCase);
} else { } else {
commandService.send(sender, MessageKey.ERROR); commandService.send(sender, MessageKey.ERROR);
} }

View File

@ -1,6 +1,8 @@
package fr.xephi.authme.command.executable.authme; package fr.xephi.authme.command.executable.authme;
import ch.jalu.injector.Injector;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.converter.Converter; import fr.xephi.authme.converter.Converter;
@ -10,7 +12,6 @@ import fr.xephi.authme.converter.RoyalAuthConverter;
import fr.xephi.authme.converter.SqliteToSql; import fr.xephi.authme.converter.SqliteToSql;
import fr.xephi.authme.converter.vAuthConverter; import fr.xephi.authme.converter.vAuthConverter;
import fr.xephi.authme.converter.xAuthConverter; import fr.xephi.authme.converter.xAuthConverter;
import fr.xephi.authme.initialization.AuthMeServiceInitializer;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.BukkitService;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -30,7 +31,7 @@ public class ConverterCommand implements ExecutableCommand {
private BukkitService bukkitService; private BukkitService bukkitService;
@Inject @Inject
private AuthMeServiceInitializer initializer; private Injector injector;
@Override @Override
public void executeCommand(final CommandSender sender, List<String> arguments) { public void executeCommand(final CommandSender sender, List<String> arguments) {
@ -45,13 +46,17 @@ public class ConverterCommand implements ExecutableCommand {
} }
// Get the proper converter instance // Get the proper converter instance
final Converter converter = initializer.newInstance(jobType.getConverterClass()); final Converter converter = injector.newInstance(jobType.getConverterClass());
// Run the convert job // Run the convert job
bukkitService.runTaskAsynchronously(new Runnable() { bukkitService.runTaskAsynchronously(new Runnable() {
@Override @Override
public void run() { public void run() {
converter.execute(sender); try {
converter.execute(sender);
} catch (Exception e) {
ConsoleLogger.logException("Error during conversion:", e);
}
} }
}); });

View File

@ -1,11 +1,8 @@
package fr.xephi.authme.command.executable.authme; package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.task.purge.PurgeService;
import fr.xephi.authme.task.PurgeTask;
import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.BukkitService;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -21,10 +18,7 @@ import java.util.Set;
public class PurgeBannedPlayersCommand implements ExecutableCommand { public class PurgeBannedPlayersCommand implements ExecutableCommand {
@Inject @Inject
private DataSource dataSource; private PurgeService purgeService;
@Inject
private AuthMe plugin;
@Inject @Inject
private BukkitService bukkitService; private BukkitService bukkitService;
@ -32,18 +26,12 @@ public class PurgeBannedPlayersCommand implements ExecutableCommand {
@Override @Override
public void executeCommand(CommandSender sender, List<String> arguments) { public void executeCommand(CommandSender sender, List<String> arguments) {
// Get the list of banned players // Get the list of banned players
Set<String> namedBanned = new HashSet<>();
Set<OfflinePlayer> bannedPlayers = bukkitService.getBannedPlayers(); Set<OfflinePlayer> bannedPlayers = bukkitService.getBannedPlayers();
Set<String> namedBanned = new HashSet<>(bannedPlayers.size());
for (OfflinePlayer offlinePlayer : bannedPlayers) { for (OfflinePlayer offlinePlayer : bannedPlayers) {
namedBanned.add(offlinePlayer.getName().toLowerCase()); namedBanned.add(offlinePlayer.getName().toLowerCase());
} }
//todo: note this should may run async because it may executes a SQL-Query purgeService.purgePlayers(sender, namedBanned, bannedPlayers.toArray(new OfflinePlayer[bannedPlayers.size()]));
// Purge the banned players
dataSource.purgeBanned(namedBanned);
// Show a status message
sender.sendMessage(ChatColor.GOLD + "Purging user accounts...");
new PurgeTask(plugin, sender, namedBanned, bannedPlayers).runTaskTimer(plugin, 0, 1);
} }
} }

View File

@ -1,16 +1,13 @@
package fr.xephi.authme.command.executable.authme; package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.task.purge.PurgeService;
import fr.xephi.authme.task.PurgeTask;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.Calendar; import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* Command for purging the data of players which have not been since for a given number * Command for purging the data of players which have not been since for a given number
@ -21,10 +18,7 @@ public class PurgeCommand implements ExecutableCommand {
private static final int MINIMUM_LAST_SEEN_DAYS = 30; private static final int MINIMUM_LAST_SEEN_DAYS = 30;
@Inject @Inject
private DataSource dataSource; private PurgeService purgeService;
@Inject
private AuthMe plugin;
@Override @Override
public void executeCommand(CommandSender sender, List<String> arguments) { public void executeCommand(CommandSender sender, List<String> arguments) {
@ -52,13 +46,7 @@ public class PurgeCommand implements ExecutableCommand {
calendar.add(Calendar.DATE, -days); calendar.add(Calendar.DATE, -days);
long until = calendar.getTimeInMillis(); long until = calendar.getTimeInMillis();
//todo: note this should may run async because it may executes a SQL-Query // Run the purge
// Purge the data, get the purged values purgeService.runPurge(sender, until);
Set<String> purged = dataSource.autoPurgeDatabase(until);
// Show a status message
sender.sendMessage(ChatColor.GOLD + "Deleted " + purged.size() + " user accounts");
sender.sendMessage(ChatColor.GOLD + "Purging user accounts...");
new PurgeTask(plugin, sender, purged).runTaskTimer(plugin, 0, 1);
} }
} }

View File

@ -2,6 +2,7 @@ package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.limbo.LimboCache;
import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
@ -37,6 +38,9 @@ public class RegisterAdminCommand implements ExecutableCommand {
@Inject @Inject
private ValidationService validationService; private ValidationService validationService;
@Inject
private LimboCache limboCache;
@Override @Override
public void executeCommand(final CommandSender sender, List<String> arguments) { public void executeCommand(final CommandSender sender, List<String> arguments) {
// Get the player name and password // Get the player name and password
@ -79,7 +83,8 @@ public class RegisterAdminCommand implements ExecutableCommand {
bukkitService.scheduleSyncDelayedTask(new Runnable() { bukkitService.scheduleSyncDelayedTask(new Runnable() {
@Override @Override
public void run() { public void run() {
player.kickPlayer("An admin just registered you, please log in again"); limboCache.restoreData(player);
player.kickPlayer(commandService.retrieveSingle(MessageKey.KICK_FOR_ADMIN_REGISTER));
} }
}); });
} }

View File

@ -1,17 +1,20 @@
package fr.xephi.authme.command.executable.authme; package fr.xephi.authme.command.executable.authme;
import ch.jalu.injector.Injector;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.AuthMeServiceInitializer; import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.Collection;
import java.util.List; import java.util.List;
/** /**
@ -23,10 +26,10 @@ public class ReloadCommand implements ExecutableCommand {
private AuthMe plugin; private AuthMe plugin;
@Inject @Inject
private AuthMeServiceInitializer initializer; private Injector injector;
@Inject @Inject
private NewSetting settings; private Settings settings;
@Inject @Inject
private DataSource dataSource; private DataSource dataSource;
@ -44,7 +47,7 @@ public class ReloadCommand implements ExecutableCommand {
ConsoleLogger.info("Note: cannot change database type during /authme reload"); ConsoleLogger.info("Note: cannot change database type during /authme reload");
sender.sendMessage("Note: cannot change database type during /authme reload"); sender.sendMessage("Note: cannot change database type during /authme reload");
} }
initializer.performReloadOnServices(); performReloadOnServices();
commandService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS); commandService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS);
} catch (Exception e) { } catch (Exception e) {
sender.sendMessage("Error occurred during reload of AuthMe: aborting"); sender.sendMessage("Error occurred during reload of AuthMe: aborting");
@ -52,4 +55,16 @@ public class ReloadCommand implements ExecutableCommand {
plugin.stopOrUnload(); plugin.stopOrUnload();
} }
} }
private void performReloadOnServices() {
Collection<Reloadable> reloadables = injector.retrieveAllOfType(Reloadable.class);
for (Reloadable reloadable : reloadables) {
reloadable.reload();
}
Collection<SettingsDependent> settingsDependents = injector.retrieveAllOfType(SettingsDependent.class);
for (SettingsDependent dependent : settingsDependents) {
dependent.reload(settings);
}
}
} }

View File

@ -1,29 +1,17 @@
package fr.xephi.authme.command.executable.authme; package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.cache.limbo.LimboCache;
import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.permission.AuthGroupHandler; import fr.xephi.authme.process.Management;
import fr.xephi.authme.permission.AuthGroupType;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.task.LimboPlayerTaskManager;
import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.Utils;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.List; import java.util.List;
import static fr.xephi.authme.util.BukkitService.TICKS_PER_SECOND;
/** /**
* Admin command to unregister a player. * Admin command to unregister a player.
*/ */
@ -35,74 +23,27 @@ public class UnregisterAdminCommand implements ExecutableCommand {
@Inject @Inject
private CommandService commandService; private CommandService commandService;
@Inject
private PlayerCache playerCache;
@Inject @Inject
private BukkitService bukkitService; private BukkitService bukkitService;
@Inject @Inject
private LimboCache limboCache; private Management management;
@Inject
private LimboPlayerTaskManager limboPlayerTaskManager;
@Inject
private AuthGroupHandler authGroupHandler;
UnregisterAdminCommand() {
}
@Override @Override
public void executeCommand(final CommandSender sender, List<String> arguments) { public void executeCommand(final CommandSender sender, List<String> arguments) {
// Get the player name
String playerName = arguments.get(0); String playerName = arguments.get(0);
String playerNameLowerCase = playerName.toLowerCase();
// Make sure the user is valid // Make sure the user exists
if (!dataSource.isAuthAvailable(playerNameLowerCase)) { if (!dataSource.isAuthAvailable(playerName)) {
commandService.send(sender, MessageKey.UNKNOWN_USER); commandService.send(sender, MessageKey.UNKNOWN_USER);
return; return;
} }
// Remove the player // Get the player from the server and perform unregister
if (!dataSource.removeAuth(playerNameLowerCase)) { Player target = bukkitService.getPlayerExact(playerName);
commandService.send(sender, MessageKey.ERROR); management.performUnregisterByAdmin(sender, playerName, target);
return;
}
// Unregister the player
Player target = bukkitService.getPlayerExact(playerNameLowerCase);
playerCache.removePlayer(playerNameLowerCase);
authGroupHandler.setGroup(target, AuthGroupType.UNREGISTERED);
if (target != null && target.isOnline()) {
if (commandService.getProperty(RegistrationSettings.FORCE)) {
applyUnregisteredEffectsAndTasks(target);
}
commandService.send(target, MessageKey.UNREGISTERED_SUCCESS);
}
// Show a status message
commandService.send(sender, MessageKey.UNREGISTERED_SUCCESS);
ConsoleLogger.info(sender.getName() + " unregistered " + playerName);
}
/**
* When registration is forced, applies the configured "unregistered effects" to the player as he
* would encounter when joining the server before logging on - reminder task to log in,
* timeout kick, blindness.
*
* @param target the player that was unregistered
*/
private void applyUnregisteredEffectsAndTasks(Player target) {
// TODO #765: Remove use of Utils method and behave according to settings
Utils.teleportToSpawn(target);
limboCache.addLimboPlayer(target);
limboPlayerTaskManager.registerTimeoutTask(target);
limboPlayerTaskManager.registerMessageTask(target.getName(), false);
final int timeout = commandService.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND;
if (commandService.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) {
target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeout, 2));
}
} }
} }

View File

@ -31,11 +31,13 @@ public class VersionCommand implements ExecutableCommand {
+ " v" + AuthMe.getPluginVersion() + ChatColor.GRAY + " (build: " + AuthMe.getPluginBuildNumber() + ")"); + " v" + AuthMe.getPluginVersion() + ChatColor.GRAY + " (build: " + AuthMe.getPluginBuildNumber() + ")");
sender.sendMessage(ChatColor.GOLD + "Developers:"); sender.sendMessage(ChatColor.GOLD + "Developers:");
Collection<? extends Player> onlinePlayers = bukkitService.getOnlinePlayers(); Collection<? extends Player> onlinePlayers = bukkitService.getOnlinePlayers();
printDeveloper(sender, "Xephi", "xephi59", "Lead Developer", onlinePlayers); printDeveloper(sender, "Alexandre Vanhecke", "xephi59", "Original Author", onlinePlayers);
printDeveloper(sender, "Lucas J.", "ljacqu", "Main Developer", onlinePlayers);
printDeveloper(sender, "Gnat008", "gnat008", "Developer", onlinePlayers);
printDeveloper(sender, "DNx5", "DNx5", "Developer", onlinePlayers); printDeveloper(sender, "DNx5", "DNx5", "Developer", onlinePlayers);
printDeveloper(sender, "games647", "games647", "Developer", onlinePlayers); printDeveloper(sender, "games647", "games647", "Developer", onlinePlayers);
printDeveloper(sender, "Tim Visee", "timvisee", "Developer", onlinePlayers); printDeveloper(sender, "Tim Visee", "timvisee", "Developer", onlinePlayers);
printDeveloper(sender, "Sgdc3", "sgdc3", "Project manager, Contributor", onlinePlayers); printDeveloper(sender, "Gabriele C.", "sgdc3", "Project manager, Contributor", onlinePlayers);
sender.sendMessage(ChatColor.GOLD + "Website: " + ChatColor.WHITE + sender.sendMessage(ChatColor.GOLD + "Website: " + ChatColor.WHITE +
"http://dev.bukkit.org/bukkit-plugins/authme-reloaded/"); "http://dev.bukkit.org/bukkit-plugins/authme-reloaded/");
sender.sendMessage(ChatColor.GOLD + "License: " + ChatColor.WHITE + "GNU GPL v3.0" sender.sendMessage(ChatColor.GOLD + "License: " + ChatColor.WHITE + "GNU GPL v3.0"

View File

@ -47,7 +47,6 @@ public class ChangePasswordCommand extends PlayerCommand {
return; return;
} }
// TODO ljacqu 20160117: Call async task via Management
management.performPasswordChange(player, oldPassword, newPassword); management.performPasswordChange(player, oldPassword, newPassword);
} }
} }

View File

@ -1,18 +1,17 @@
package fr.xephi.authme.command.executable.email; package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.mail.SendMailSSL;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.RandomString; import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import javax.inject.Inject; import javax.inject.Inject;
@ -33,53 +32,39 @@ public class RecoverEmailCommand extends PlayerCommand {
private PlayerCache playerCache; private PlayerCache playerCache;
@Inject @Inject
// TODO #655: Remove injected AuthMe instance once Authme#mail is encapsulated private SendMailSSL sendMailSsl;
private AuthMe plugin;
@Override @Override
public void runCommand(Player player, List<String> arguments) { public void runCommand(Player player, List<String> arguments) {
final String playerMail = arguments.get(0); final String playerMail = arguments.get(0);
final String playerName = player.getName(); final String playerName = player.getName();
if (plugin.mail == null) { if (!sendMailSsl.hasAllInformation()) {
ConsoleLogger.showError("Mail API is not set"); ConsoleLogger.warning("Mail API is not set");
commandService.send(player, MessageKey.ERROR); commandService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
return; return;
} }
if (dataSource.isAuthAvailable(playerName)) { if (playerCache.isAuthenticated(playerName)) {
if (PlayerCache.getInstance().isAuthenticated(playerName)) { commandService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
commandService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); return;
return;
}
String thePass = RandomString.generate(commandService.getProperty(EmailSettings.RECOVERY_PASSWORD_LENGTH));
HashedPassword hashNew = passwordSecurity.computeHash(thePass, playerName);
PlayerAuth auth;
if (playerCache.isAuthenticated(playerName)) {
auth = playerCache.getAuth(playerName);
} else if (dataSource.isAuthAvailable(playerName)) {
auth = dataSource.getAuth(playerName);
} else {
commandService.send(player, MessageKey.UNKNOWN_USER);
return;
}
if (StringUtils.isEmpty(commandService.getProperty(EmailSettings.MAIL_ACCOUNT))) {
ConsoleLogger.showError("No mail account set in settings");
commandService.send(player, MessageKey.ERROR);
return;
}
if (!playerMail.equalsIgnoreCase(auth.getEmail()) || "your@email.com".equalsIgnoreCase(playerMail)
|| "your@email.com".equalsIgnoreCase(auth.getEmail())) {
commandService.send(player, MessageKey.INVALID_EMAIL);
return;
}
auth.setPassword(hashNew);
dataSource.updatePassword(auth);
plugin.mail.main(auth, thePass);
commandService.send(player, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE);
} else {
commandService.send(player, MessageKey.REGISTER_EMAIL_MESSAGE);
} }
PlayerAuth auth = dataSource.getAuth(playerName);
if (auth == null) {
commandService.send(player, MessageKey.REGISTER_EMAIL_MESSAGE);
return;
}
if (!playerMail.equalsIgnoreCase(auth.getEmail()) || "your@email.com".equalsIgnoreCase(auth.getEmail())) {
commandService.send(player, MessageKey.INVALID_EMAIL);
return;
}
String thePass = RandomString.generate(commandService.getProperty(EmailSettings.RECOVERY_PASSWORD_LENGTH));
HashedPassword hashNew = passwordSecurity.computeHash(thePass, playerName);
auth.setPassword(hashNew);
dataSource.updatePassword(auth);
sendMailSsl.sendPasswordMail(auth, thePass);
commandService.send(player, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE);
} }
} }

View File

@ -3,6 +3,7 @@ package fr.xephi.authme.command.executable.register;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.mail.SendMailSSL;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.HashAlgorithm;
@ -27,6 +28,9 @@ public class RegisterCommand extends PlayerCommand {
@Inject @Inject
private CommandService commandService; private CommandService commandService;
@Inject
private SendMailSSL sendMailSsl;
@Override @Override
public void runCommand(Player player, List<String> arguments) { public void runCommand(Player player, List<String> arguments) {
if (commandService.getProperty(SecuritySettings.PASSWORD_HASH) == HashAlgorithm.TWO_FACTOR) { if (commandService.getProperty(SecuritySettings.PASSWORD_HASH) == HashAlgorithm.TWO_FACTOR) {
@ -63,11 +67,10 @@ public class RegisterCommand extends PlayerCommand {
} }
private void handleEmailRegistration(Player player, List<String> arguments) { private void handleEmailRegistration(Player player, List<String> arguments) {
if (commandService.getProperty(EmailSettings.MAIL_ACCOUNT).isEmpty()) { if (!sendMailSsl.hasAllInformation()) {
player.sendMessage("Cannot register: no email address is set for the server. " commandService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
+ "Please contact an administrator"); ConsoleLogger.warning("Cannot register player '" + player.getName() + "': no email or password is set "
ConsoleLogger.showError("Cannot register player '" + player.getName() + "': no email is set " + "to send emails from. Please adjust your config at " + EmailSettings.MAIL_ACCOUNT.getPath());
+ "to send emails from. Please add one in your config at " + EmailSettings.MAIL_ACCOUNT.getPath());
return; return;
} }

View File

@ -10,6 +10,9 @@ import org.bukkit.entity.Player;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.List; import java.util.List;
/**
* Command for a player to unregister himself.
*/
public class UnregisterCommand extends PlayerCommand { public class UnregisterCommand extends PlayerCommand {
@Inject @Inject
@ -24,15 +27,15 @@ public class UnregisterCommand extends PlayerCommand {
@Override @Override
public void runCommand(Player player, List<String> arguments) { public void runCommand(Player player, List<String> arguments) {
String playerPass = arguments.get(0); String playerPass = arguments.get(0);
final String playerNameLowerCase = player.getName().toLowerCase(); final String playerName = player.getName();
// Make sure the player is authenticated // Make sure the player is authenticated
if (!playerCache.isAuthenticated(playerNameLowerCase)) { if (!playerCache.isAuthenticated(playerName)) {
commandService.send(player, MessageKey.NOT_LOGGED_IN); commandService.send(player, MessageKey.NOT_LOGGED_IN);
return; return;
} }
// Unregister the player // Unregister the player
management.performUnregister(player, playerPass, false); management.performUnregister(player, playerPass);
} }
} }

View File

@ -10,7 +10,7 @@ import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.permission.DefaultPermission; import fr.xephi.authme.permission.DefaultPermission;
import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.PluginSettings;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -48,9 +48,9 @@ public class HelpProvider implements SettingsDependent {
private String helpHeader; private String helpHeader;
@Inject @Inject
HelpProvider(PermissionsManager permissionsManager, NewSetting settings) { HelpProvider(PermissionsManager permissionsManager, Settings settings) {
this.permissionsManager = permissionsManager; this.permissionsManager = permissionsManager;
loadSettings(settings); reload(settings);
} }
private List<String> printHelp(CommandSender sender, FoundCommandResult result, int options) { private List<String> printHelp(CommandSender sender, FoundCommandResult result, int options) {
@ -102,7 +102,7 @@ public class HelpProvider implements SettingsDependent {
} }
@Override @Override
public void loadSettings(NewSetting settings) { public void reload(Settings settings) {
helpHeader = settings.getProperty(PluginSettings.HELP_HEADER); helpHeader = settings.getProperty(PluginSettings.HELP_HEADER);
} }

View File

@ -4,7 +4,7 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.ConverterSettings; import fr.xephi.authme.settings.properties.ConverterSettings;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -20,11 +20,11 @@ import java.io.IOException;
public class CrazyLoginConverter implements Converter { public class CrazyLoginConverter implements Converter {
private final DataSource database; private final DataSource database;
private final NewSetting settings; private final Settings settings;
private final File dataFolder; private final File dataFolder;
@Inject @Inject
CrazyLoginConverter(@DataFolder File dataFolder, DataSource dataSource, NewSetting settings) { CrazyLoginConverter(@DataFolder File dataFolder, DataSource dataSource, Settings settings) {
this.dataFolder = dataFolder; this.dataFolder = dataFolder;
this.database = dataSource; this.database = dataSource;
this.settings = settings; this.settings = settings;
@ -61,7 +61,7 @@ public class CrazyLoginConverter implements Converter {
} }
ConsoleLogger.info("CrazyLogin database has been imported correctly"); ConsoleLogger.info("CrazyLogin database has been imported correctly");
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError("Can't open the crazylogin database file! Does it exist?"); ConsoleLogger.warning("Can't open the crazylogin database file! Does it exist?");
ConsoleLogger.logException("Encountered", ex); ConsoleLogger.logException("Encountered", ex);
} }
} }

View File

@ -43,7 +43,7 @@ public class ForceFlatToSqlite {
} }
if (!skippedPlayers.isEmpty()) { if (!skippedPlayers.isEmpty()) {
ConsoleLogger.showError("Warning: skipped conversion for players which were already in SQLite: " ConsoleLogger.warning("Warning: skipped conversion for players which were already in SQLite: "
+ StringUtils.join(", ", skippedPlayers)); + StringUtils.join(", ", skippedPlayers));
} }
ConsoleLogger.info("Database successfully converted from " + source.getClass().getSimpleName() ConsoleLogger.info("Database successfully converted from " + source.getClass().getSimpleName()

View File

@ -6,7 +6,7 @@ import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.ConverterSettings; import fr.xephi.authme.settings.properties.ConverterSettings;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -24,12 +24,12 @@ import java.util.Map.Entry;
public class RakamakConverter implements Converter { public class RakamakConverter implements Converter {
private final DataSource database; private final DataSource database;
private final NewSetting settings; private final Settings settings;
private final File pluginFolder; private final File pluginFolder;
private final PasswordSecurity passwordSecurity; private final PasswordSecurity passwordSecurity;
@Inject @Inject
RakamakConverter(@DataFolder File dataFolder, DataSource dataSource, NewSetting settings, RakamakConverter(@DataFolder File dataFolder, DataSource dataSource, Settings settings,
PasswordSecurity passwordSecurity) { PasswordSecurity passwordSecurity) {
this.database = dataSource; this.database = dataSource;
this.settings = settings; this.settings = settings;
@ -82,15 +82,14 @@ public class RakamakConverter implements Converter {
.realName(playerName) .realName(playerName)
.ip(ip) .ip(ip)
.password(psw) .password(psw)
.lastLogin(System.currentTimeMillis()) .lastLogin(0)
.build(); .build();
database.saveAuth(auth); database.saveAuth(auth);
} }
ConsoleLogger.info("Rakamak database has been imported correctly"); ConsoleLogger.info("Rakamak database has been imported correctly");
sender.sendMessage("Rakamak database has been imported correctly"); sender.sendMessage("Rakamak database has been imported correctly");
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.logException("Can't open the rakamak database file! Does it exist?", ex);
sender.sendMessage("Can't open the rakamak database file! Does it exist?");
} }
} }
} }

View File

@ -7,19 +7,19 @@ import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.datasource.SQLite; import fr.xephi.authme.datasource.SQLite;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages; import fr.xephi.authme.output.Messages;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import javax.inject.Inject; import javax.inject.Inject;
public class SqliteToSql implements Converter { public class SqliteToSql implements Converter {
private final NewSetting settings; private final Settings settings;
private final DataSource dataSource; private final DataSource dataSource;
private final Messages messages; private final Messages messages;
@Inject @Inject
SqliteToSql(NewSetting settings, DataSource dataSource, Messages messages) { SqliteToSql(Settings settings, DataSource dataSource, Messages messages) {
this.settings = settings; this.settings = settings;
this.dataSource = dataSource; this.dataSource = dataSource;
this.messages = messages; this.messages = messages;

View File

@ -1,28 +1,77 @@
package fr.xephi.authme.converter; package fr.xephi.authme.converter;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.DataFolder;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import javax.inject.Inject; import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
import java.util.UUID;
import static fr.xephi.authme.util.StringUtils.makePath;
public class vAuthConverter implements Converter { public class vAuthConverter implements Converter {
private final AuthMe plugin; private final DataSource dataSource;
private final File vAuthPasswordsFile;
@Inject @Inject
vAuthConverter(AuthMe plugin) { vAuthConverter(@DataFolder File dataFolder, DataSource dataSource) {
this.plugin = plugin; vAuthPasswordsFile = new File(dataFolder.getParent(), makePath("vAuth", "passwords.yml"));
this.dataSource = dataSource;
} }
@Override @Override
public void execute(CommandSender sender) { public void execute(CommandSender sender) {
try { try (Scanner scanner = new Scanner(vAuthPasswordsFile)) {
new vAuthFileReader(plugin).convert(); while (scanner.hasNextLine()) {
} catch (Exception e) { String line = scanner.nextLine();
sender.sendMessage(e.getMessage()); String name = line.split(": ")[0];
ConsoleLogger.showError(e.getMessage()); String password = line.split(": ")[1];
PlayerAuth auth;
if (isUuidInstance(password)) {
String pname;
try {
pname = Bukkit.getOfflinePlayer(UUID.fromString(name)).getName();
} catch (Exception | NoSuchMethodError e) {
pname = getName(UUID.fromString(name));
}
if (pname == null)
continue;
auth = PlayerAuth.builder()
.name(pname.toLowerCase())
.realName(pname)
.password(password, null).build();
} else {
auth = PlayerAuth.builder()
.name(name.toLowerCase())
.realName(name)
.password(password, null).build();
}
dataSource.saveAuth(auth);
}
} catch (IOException e) {
ConsoleLogger.logException("Error while trying to import some vAuth data", e);
} }
} }
private static boolean isUuidInstance(String s) {
return s.length() > 8 && s.charAt(8) == '-';
}
private String getName(UUID uuid) {
for (OfflinePlayer op : Bukkit.getOfflinePlayers()) {
if (op.getUniqueId().compareTo(uuid) == 0) {
return op.getName();
}
}
return null;
}
} }

View File

@ -1,83 +0,0 @@
package fr.xephi.authme.converter;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
import java.util.UUID;
import static fr.xephi.authme.util.StringUtils.makePath;
class vAuthFileReader {
private final AuthMe plugin;
private final DataSource database;
/**
* Constructor for vAuthFileReader.
*
* @param plugin AuthMe
*/
public vAuthFileReader(AuthMe plugin) {
this.plugin = plugin;
this.database = plugin.getDataSource();
}
public void convert() {
final File file = new File(plugin.getDataFolder().getParent(), makePath("vAuth", "passwords.yml"));
Scanner scanner;
try {
scanner = new Scanner(file);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
String name = line.split(": ")[0];
String password = line.split(": ")[1];
PlayerAuth auth;
if (isUuidInstance(password)) {
String pname;
try {
pname = Bukkit.getOfflinePlayer(UUID.fromString(name)).getName();
} catch (Exception | NoSuchMethodError e) {
pname = getName(UUID.fromString(name));
}
if (pname == null)
continue;
auth = PlayerAuth.builder()
.name(pname.toLowerCase())
.realName(pname)
.password(password, null).build();
} else {
auth = PlayerAuth.builder()
.name(name.toLowerCase())
.realName(name)
.password(password, null).build();
}
database.saveAuth(auth);
}
scanner.close();
} catch (IOException e) {
ConsoleLogger.logException("Error while trying to import some vAuth data", e);
}
}
private static boolean isUuidInstance(String s) {
return s.length() > 8 && s.charAt(8) == '-';
}
private String getName(UUID uuid) {
for (OfflinePlayer op : Bukkit.getOfflinePlayers()) {
if (op.getUniqueId().compareTo(uuid) == 0) {
return op.getName();
}
}
return null;
}
}

View File

@ -1,28 +1,146 @@
package fr.xephi.authme.converter; package fr.xephi.authme.converter;
import fr.xephi.authme.AuthMe; import de.luricos.bukkit.xAuth.database.DatabaseTables;
import de.luricos.bukkit.xAuth.utils.xAuthLog;
import de.luricos.bukkit.xAuth.xAuth;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.util.CollectionUtils;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.plugin.PluginManager;
import javax.inject.Inject; import javax.inject.Inject;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import static fr.xephi.authme.util.StringUtils.makePath;
public class xAuthConverter implements Converter { public class xAuthConverter implements Converter {
private final AuthMe plugin;
@Inject @Inject
xAuthConverter(AuthMe plugin) { @DataFolder
this.plugin = plugin; private File dataFolder;
@Inject
private DataSource database;
@Inject
private PluginManager pluginManager;
xAuthConverter() {
} }
@Override @Override
public void execute(CommandSender sender) { public void execute(CommandSender sender) {
try { try {
Class.forName("de.luricos.bukkit.xAuth.xAuth"); Class.forName("de.luricos.bukkit.xAuth.xAuth");
xAuthToFlat converter = new xAuthToFlat(plugin, sender); convert(sender);
converter.convert();
} catch (ClassNotFoundException ce) { } catch (ClassNotFoundException ce) {
sender.sendMessage("xAuth has not been found, please put xAuth.jar in your plugin folder and restart!"); sender.sendMessage("xAuth has not been found, please put xAuth.jar in your plugin folder and restart!");
} }
} }
private void convert(CommandSender sender) {
if (pluginManager.getPlugin("xAuth") == null) {
sender.sendMessage("[AuthMe] xAuth plugin not found");
return;
}
// TODO ljacqu 20160702: xAuthDb is not used except for the existence check -- is this intended?
File xAuthDb = new File(dataFolder.getParent(), makePath("xAuth", "xAuth.h2.db"));
if (!xAuthDb.exists()) {
sender.sendMessage("[AuthMe] xAuth H2 database not found, checking for MySQL or SQLite data...");
}
List<Integer> players = getXAuthPlayers();
if (CollectionUtils.isEmpty(players)) {
sender.sendMessage("[AuthMe] Error while importing xAuthPlayers: did not find any players");
return;
}
sender.sendMessage("[AuthMe] Starting import...");
for (int id : players) {
String pl = getIdPlayer(id);
String psw = getPassword(id);
if (psw != null && !psw.isEmpty() && pl != null) {
PlayerAuth auth = PlayerAuth.builder()
.name(pl.toLowerCase())
.realName(pl)
.password(psw, null).build();
database.saveAuth(auth);
}
}
sender.sendMessage("[AuthMe] Successfully converted from xAuth database");
}
private String getIdPlayer(int id) {
String realPass = "";
Connection conn = xAuth.getPlugin().getDatabaseController().getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = String.format("SELECT `playername` FROM `%s` WHERE `id` = ?",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql);
ps.setInt(1, id);
rs = ps.executeQuery();
if (!rs.next())
return null;
realPass = rs.getString("playername").toLowerCase();
} catch (SQLException e) {
xAuthLog.severe("Failed to retrieve name for account: " + id, e);
return null;
} finally {
xAuth.getPlugin().getDatabaseController().close(conn, ps, rs);
}
return realPass;
}
private List<Integer> getXAuthPlayers() {
List<Integer> xP = new ArrayList<>();
Connection conn = xAuth.getPlugin().getDatabaseController().getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = String.format("SELECT * FROM `%s`",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while (rs.next()) {
xP.add(rs.getInt("id"));
}
} catch (SQLException e) {
xAuthLog.severe("Cannot import xAuthPlayers", e);
return new ArrayList<>();
} finally {
xAuth.getPlugin().getDatabaseController().close(conn, ps, rs);
}
return xP;
}
private String getPassword(int accountId) {
String realPass = "";
Connection conn = xAuth.getPlugin().getDatabaseController().getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = String.format("SELECT `password`, `pwtype` FROM `%s` WHERE `id` = ?",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql);
ps.setInt(1, accountId);
rs = ps.executeQuery();
if (!rs.next())
return null;
realPass = rs.getString("password");
} catch (SQLException e) {
xAuthLog.severe("Failed to retrieve password hash for account: " + accountId, e);
return null;
} finally {
xAuth.getPlugin().getDatabaseController().close(conn, ps, rs);
}
return realPass;
}
} }

View File

@ -1,136 +0,0 @@
package fr.xephi.authme.converter;
import de.luricos.bukkit.xAuth.database.DatabaseTables;
import de.luricos.bukkit.xAuth.utils.xAuthLog;
import de.luricos.bukkit.xAuth.xAuth;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.util.CollectionUtils;
import org.bukkit.command.CommandSender;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
class xAuthToFlat {
private final AuthMe instance;
private final DataSource database;
private final CommandSender sender;
public xAuthToFlat(AuthMe instance, CommandSender sender) {
this.instance = instance;
this.database = instance.getDataSource();
this.sender = sender;
}
public boolean convert() {
if (instance.getServer().getPluginManager().getPlugin("xAuth") == null) {
sender.sendMessage("[AuthMe] xAuth plugin not found");
return false;
}
File xAuthDb = new File(instance.getDataFolder().getParent(), "xAuth" + File.separator + "xAuth.h2.db");
if (!xAuthDb.exists()) {
sender.sendMessage("[AuthMe] xAuth H2 database not found, checking for MySQL or SQLite data...");
}
List<Integer> players = getXAuthPlayers();
if (CollectionUtils.isEmpty(players)) {
sender.sendMessage("[AuthMe] Error while importing xAuthPlayers: did not find any players");
return false;
}
sender.sendMessage("[AuthMe] Starting import...");
try {
for (int id : players) {
String pl = getIdPlayer(id);
String psw = getPassword(id);
if (psw != null && !psw.isEmpty() && pl != null) {
PlayerAuth auth = PlayerAuth.builder()
.name(pl.toLowerCase())
.realName(pl)
.password(psw, null).build();
database.saveAuth(auth);
}
}
sender.sendMessage("[AuthMe] Successfully converted from xAuth database");
} catch (Exception e) {
sender.sendMessage("[AuthMe] An error has occurred while importing the xAuth database."
+ " The import may have succeeded partially.");
ConsoleLogger.logException("Error during xAuth database import", e);
}
return true;
}
private String getIdPlayer(int id) {
String realPass = "";
Connection conn = xAuth.getPlugin().getDatabaseController().getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = String.format("SELECT `playername` FROM `%s` WHERE `id` = ?",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql);
ps.setInt(1, id);
rs = ps.executeQuery();
if (!rs.next())
return null;
realPass = rs.getString("playername").toLowerCase();
} catch (SQLException e) {
xAuthLog.severe("Failed to retrieve name for account: " + id, e);
return null;
} finally {
xAuth.getPlugin().getDatabaseController().close(conn, ps, rs);
}
return realPass;
}
private List<Integer> getXAuthPlayers() {
List<Integer> xP = new ArrayList<>();
Connection conn = xAuth.getPlugin().getDatabaseController().getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = String.format("SELECT * FROM `%s`",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while (rs.next()) {
xP.add(rs.getInt("id"));
}
} catch (SQLException e) {
xAuthLog.severe("Cannot import xAuthPlayers", e);
return new ArrayList<>();
} finally {
xAuth.getPlugin().getDatabaseController().close(conn, ps, rs);
}
return xP;
}
private String getPassword(int accountId) {
String realPass = "";
Connection conn = xAuth.getPlugin().getDatabaseController().getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = String.format("SELECT `password`, `pwtype` FROM `%s` WHERE `id` = ?",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql);
ps.setInt(1, accountId);
rs = ps.executeQuery();
if (!rs.next())
return null;
realPass = rs.getString("password");
} catch (SQLException e) {
xAuthLog.severe("Failed to retrieve password hash for account: " + accountId, e);
return null;
} finally {
xAuth.getPlugin().getDatabaseController().close(conn, ps, rs);
}
return realPass;
}
}

View File

@ -15,6 +15,7 @@ import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@ -54,7 +55,6 @@ public class CacheDataSource implements DataSource {
return executorService.submit(new Callable<Optional<PlayerAuth>>() { return executorService.submit(new Callable<Optional<PlayerAuth>>() {
@Override @Override
public Optional<PlayerAuth> call() { public Optional<PlayerAuth> call() {
ConsoleLogger.debug("REFRESH " + key);
return load(key); return load(key);
} }
}); });
@ -139,13 +139,8 @@ public class CacheDataSource implements DataSource {
} }
@Override @Override
public Set<String> autoPurgeDatabase(long until) { public Set<String> getRecordsToPurge(long until) {
Set<String> cleared = source.autoPurgeDatabase(until); return source.getRecordsToPurge(until);
for (String name : cleared) {
cachedAuths.invalidate(name);
}
return cleared;
} }
@Override @Override
@ -160,14 +155,14 @@ public class CacheDataSource implements DataSource {
@Override @Override
public void close() { public void close() {
source.close();
cachedAuths.invalidateAll();
executorService.shutdown(); executorService.shutdown();
try { try {
executorService.awaitTermination(5, TimeUnit.SECONDS); executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) { } catch (InterruptedException e) {
ConsoleLogger.writeStackTrace(e); ConsoleLogger.logException("Could not close executor service:", e);
} }
cachedAuths.invalidateAll();
source.close();
} }
@Override @Override
@ -190,8 +185,8 @@ public class CacheDataSource implements DataSource {
} }
@Override @Override
public void purgeBanned(final Set<String> banned) { public void purgeRecords(final Collection<String> banned) {
source.purgeBanned(banned); source.purgeRecords(banned);
cachedAuths.invalidateAll(banned); cachedAuths.invalidateAll(banned);
} }
@ -235,15 +230,6 @@ public class CacheDataSource implements DataSource {
return result; return result;
} }
@Override
public boolean updateIp(String user, String ip) {
boolean result = source.updateIp(user, ip);
if (result) {
cachedAuths.refresh(user);
}
return result;
}
@Override @Override
public List<PlayerAuth> getAllAuths() { public List<PlayerAuth> getAllAuths() {
return source.getAllAuths(); return source.getAllAuths();

View File

@ -1,6 +1,6 @@
package fr.xephi.authme.datasource; package fr.xephi.authme.datasource;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
/** /**
@ -23,7 +23,7 @@ public final class Columns {
public final String ID; public final String ID;
public final String IS_LOGGED; public final String IS_LOGGED;
public Columns(NewSetting settings) { public Columns(Settings settings) {
NAME = settings.getProperty(DatabaseSettings.MYSQL_COL_NAME); NAME = settings.getProperty(DatabaseSettings.MYSQL_COL_NAME);
REAL_NAME = settings.getProperty(DatabaseSettings.MYSQL_COL_REALNAME); REAL_NAME = settings.getProperty(DatabaseSettings.MYSQL_COL_REALNAME);
PASSWORD = settings.getProperty(DatabaseSettings.MYSQL_COL_PASSWORD); PASSWORD = settings.getProperty(DatabaseSettings.MYSQL_COL_PASSWORD);

View File

@ -4,6 +4,7 @@ import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -70,13 +71,19 @@ public interface DataSource extends Reloadable {
boolean updatePassword(String user, HashedPassword password); boolean updatePassword(String user, HashedPassword password);
/** /**
* Purge all records in the database whose last login was longer ago than * Get all records in the database whose last login was before the given time.
* the given time.
* *
* @param until The minimum last login * @param until The minimum last login
* @return The account names that have been removed * @return The account names selected to purge
*/ */
Set<String> autoPurgeDatabase(long until); Set<String> getRecordsToPurge(long until);
/**
* Purge the given players from the database.
*
* @param toPurge The players to purge
*/
void purgeRecords(Collection<String> toPurge);
/** /**
* Remove a user record from the database. * Remove a user record from the database.
@ -123,13 +130,6 @@ public interface DataSource extends Reloadable {
*/ */
void close(); void close();
/**
* Purge all given players, i.e. delete all players whose name is in the list.
*
* @param banned the list of players to delete
*/
void purgeBanned(Set<String> banned);
/** /**
* Return the data source type. * Return the data source type.
* *
@ -187,15 +187,6 @@ public interface DataSource extends Reloadable {
*/ */
boolean updateRealName(String user, String realName); boolean updateRealName(String user, String realName);
/**
* Update a player's IP address.
*
* @param user The name of the user (lowercase)
* @param ip The IP address to save
* @return True upon success, false upon failure
*/
boolean updateIp(String user, String ip);
/** /**
* Return all players of the database. * Return all players of the database.
* *

View File

@ -1,12 +1,9 @@
package fr.xephi.authme.datasource; package fr.xephi.authme.datasource;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.Settings;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
@ -17,6 +14,7 @@ import java.io.FileReader;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -41,27 +39,11 @@ public class FlatFile implements DataSource {
*/ */
private final File source; private final File source;
public FlatFile() { public FlatFile(File source) throws IOException {
AuthMe instance = AuthMe.getInstance();
source = new File(instance.getDataFolder(), "auths.db");
try {
source.createNewFile();
} catch (IOException e) {
ConsoleLogger.logException("Cannot open flatfile", e);
if (Settings.isStopEnabled) {
ConsoleLogger.showError("Can't use FLAT FILE... SHUTDOWN...");
instance.getServer().shutdown();
}
if (!Settings.isStopEnabled) {
instance.getServer().getPluginManager().disablePlugin(instance);
}
}
}
@VisibleForTesting
public FlatFile(File source) {
this.source = source; this.source = source;
if (!source.exists() && !source.createNewFile()) {
throw new IOException("Could not create file '" + source.getPath() + "'");
}
} }
@Override @Override
@ -82,7 +64,7 @@ public class FlatFile implements DataSource {
} }
} }
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return false; return false;
} finally { } finally {
silentClose(br); silentClose(br);
@ -109,7 +91,7 @@ public class FlatFile implements DataSource {
bw = new BufferedWriter(new FileWriter(source, true)); bw = new BufferedWriter(new FileWriter(source, true));
bw.write(auth.getNickname() + ":" + auth.getPassword().getHash() + ":" + auth.getIp() + ":" + auth.getLastLogin() + ":" + auth.getQuitLocX() + ":" + auth.getQuitLocY() + ":" + auth.getQuitLocZ() + ":" + auth.getWorld() + ":" + auth.getEmail() + "\n"); bw.write(auth.getNickname() + ":" + auth.getPassword().getHash() + ":" + auth.getIp() + ":" + auth.getLastLogin() + ":" + auth.getQuitLocX() + ":" + auth.getQuitLocY() + ":" + auth.getQuitLocZ() + ":" + auth.getWorld() + ":" + auth.getEmail() + "\n");
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return false; return false;
} finally { } finally {
silentClose(bw); silentClose(bw);
@ -145,7 +127,7 @@ public class FlatFile implements DataSource {
} }
} }
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return false; return false;
} finally { } finally {
silentClose(br); silentClose(br);
@ -179,7 +161,7 @@ public class FlatFile implements DataSource {
} }
} }
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return false; return false;
} finally { } finally {
silentClose(br); silentClose(br);
@ -216,7 +198,7 @@ public class FlatFile implements DataSource {
} }
} }
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return false; return false;
} finally { } finally {
silentClose(br); silentClose(br);
@ -229,11 +211,10 @@ public class FlatFile implements DataSource {
} }
@Override @Override
public Set<String> autoPurgeDatabase(long until) { public Set<String> getRecordsToPurge(long until) {
BufferedReader br = null; BufferedReader br = null;
BufferedWriter bw = null; Set<String> list = new HashSet<>();
ArrayList<String> lines = new ArrayList<>();
Set<String> cleared = new HashSet<>();
try { try {
br = new BufferedReader(new FileReader(source)); br = new BufferedReader(new FileReader(source));
String line; String line;
@ -241,25 +222,24 @@ public class FlatFile implements DataSource {
String[] args = line.split(":"); String[] args = line.split(":");
if (args.length >= 4) { if (args.length >= 4) {
if (Long.parseLong(args[3]) >= until) { if (Long.parseLong(args[3]) >= until) {
lines.add(line); list.add(args[0]);
continue; continue;
} }
} }
cleared.add(args[0]);
}
bw = new BufferedWriter(new FileWriter(source));
for (String l : lines) {
bw.write(l + "\n");
} }
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return cleared; return list;
} finally { } finally {
silentClose(br); silentClose(br);
silentClose(bw);
} }
return cleared; return list;
}
@Override
public void purgeRecords(Collection<String> toPurge) {
throw new UnsupportedOperationException("Flat file no longer supported");
} }
@Override @Override
@ -284,7 +264,7 @@ public class FlatFile implements DataSource {
bw.write(l + "\n"); bw.write(l + "\n");
} }
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return false; return false;
} finally { } finally {
silentClose(br); silentClose(br);
@ -306,7 +286,7 @@ public class FlatFile implements DataSource {
} }
} }
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return null; return null;
} finally { } finally {
silentClose(br); silentClose(br);
@ -339,10 +319,10 @@ public class FlatFile implements DataSource {
} }
} }
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return false; return false;
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return false; return false;
} finally { } finally {
if (br != null) { if (br != null) {
@ -374,10 +354,10 @@ public class FlatFile implements DataSource {
} }
return countIp; return countIp;
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return new ArrayList<>(); return new ArrayList<>();
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return new ArrayList<>(); return new ArrayList<>();
} finally { } finally {
if (br != null) { if (br != null) {
@ -404,7 +384,7 @@ public class FlatFile implements DataSource {
} }
return countEmail; return countEmail;
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
} finally { } finally {
if (br != null) { if (br != null) {
try { try {
@ -416,37 +396,6 @@ public class FlatFile implements DataSource {
return 0; return 0;
} }
@Override
public void purgeBanned(Set<String> banned) {
BufferedReader br = null;
BufferedWriter bw = null;
ArrayList<String> lines = new ArrayList<>();
try {
br = new BufferedReader(new FileReader(source));
String line;
while ((line = br.readLine()) != null) {
String[] args = line.split(":");
try {
if (banned.contains(args[0])) {
lines.add(line);
}
} catch (NullPointerException | ArrayIndexOutOfBoundsException ignored) {
}
}
bw = new BufferedWriter(new FileWriter(source));
for (String l : lines) {
bw.write(l + "\n");
}
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
} finally {
silentClose(br);
silentClose(bw);
}
}
@Override @Override
public DataSourceType getType() { public DataSourceType getType() {
return DataSourceType.FILE; return DataSourceType.FILE;
@ -479,7 +428,7 @@ public class FlatFile implements DataSource {
result++; result++;
} }
} catch (Exception ex) { } catch (Exception ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return result; return result;
} finally { } finally {
silentClose(br); silentClose(br);
@ -492,11 +441,6 @@ public class FlatFile implements DataSource {
throw new UnsupportedOperationException("Flat file no longer supported"); throw new UnsupportedOperationException("Flat file no longer supported");
} }
@Override
public boolean updateIp(String user, String ip) {
throw new UnsupportedOperationException("Flat file no longer supported");
}
@Override @Override
public List<PlayerAuth> getAllAuths() { public List<PlayerAuth> getAllAuths() {
BufferedReader br = null; BufferedReader br = null;

View File

@ -3,13 +3,11 @@ package fr.xephi.authme.datasource;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException; import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.security.crypts.XFBCRYPT; import fr.xephi.authme.security.crypts.XFBCRYPT;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.HooksSettings;
@ -25,72 +23,65 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Types; import java.sql.Types;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
public class MySQL implements DataSource { public class MySQL implements DataSource {
private final String host; private String host;
private final String port; private String port;
private final String username; private String username;
private final String password; private String password;
private final String database; private String database;
private final String tableName; private String tableName;
private final List<String> columnOthers; private List<String> columnOthers;
private final Columns col; private Columns col;
private final HashAlgorithm hashAlgorithm; private HashAlgorithm hashAlgorithm;
private HikariDataSource ds; private HikariDataSource ds;
private final String phpBbPrefix; private String phpBbPrefix;
private final int phpBbGroup; private int phpBbGroup;
private final String wordpressPrefix; private String wordpressPrefix;
public MySQL(NewSetting settings) throws ClassNotFoundException, SQLException, PoolInitializationException { public MySQL(Settings settings) throws ClassNotFoundException, SQLException, PoolInitializationException {
this.host = settings.getProperty(DatabaseSettings.MYSQL_HOST); setParameters(settings);
this.port = settings.getProperty(DatabaseSettings.MYSQL_PORT);
this.username = settings.getProperty(DatabaseSettings.MYSQL_USERNAME);
this.password = settings.getProperty(DatabaseSettings.MYSQL_PASSWORD);
this.database = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
this.columnOthers = settings.getProperty(HooksSettings.MYSQL_OTHER_USERNAME_COLS);
this.col = new Columns(settings);
this.hashAlgorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH);
this.phpBbPrefix = settings.getProperty(HooksSettings.PHPBB_TABLE_PREFIX);
this.phpBbGroup = settings.getProperty(HooksSettings.PHPBB_ACTIVATED_GROUP_ID);
this.wordpressPrefix = settings.getProperty(HooksSettings.WORDPRESS_TABLE_PREFIX);
// Set the connection arguments (and check if connection is ok) // Set the connection arguments (and check if connection is ok)
try { try {
this.setConnectionArguments(); this.setConnectionArguments();
} catch (RuntimeException e) { } catch (RuntimeException e) {
if (e instanceof IllegalArgumentException) { if (e instanceof IllegalArgumentException) {
ConsoleLogger.showError("Invalid database arguments! Please check your configuration!"); ConsoleLogger.warning("Invalid database arguments! Please check your configuration!");
ConsoleLogger.showError("If this error persists, please report it to the developer!"); ConsoleLogger.warning("If this error persists, please report it to the developer!");
throw new IllegalArgumentException(e);
} }
if (e instanceof PoolInitializationException) { if (e instanceof PoolInitializationException) {
ConsoleLogger.showError("Can't initialize database connection! Please check your configuration!"); ConsoleLogger.warning("Can't initialize database connection! Please check your configuration!");
ConsoleLogger.showError("If this error persists, please report it to the developer!"); ConsoleLogger.warning("If this error persists, please report it to the developer!");
throw new PoolInitializationException(e);
} }
ConsoleLogger.showError("Can't use the Hikari Connection Pool! Please, report this error to the developer!"); ConsoleLogger.warning("Can't use the Hikari Connection Pool! Please, report this error to the developer!");
throw e; throw e;
} }
// Initialize the database // Initialize the database
try { try {
this.setupConnection(); checkTablesAndColumns();
} catch (SQLException e) { } catch (SQLException e) {
this.close(); close();
ConsoleLogger.showError("Can't initialize the MySQL database... Please check your database settings in the config.yml file! SHUTDOWN..."); ConsoleLogger.logException("Can't initialize the MySQL database:", e);
ConsoleLogger.showError("If this error persists, please report it to the developer!"); ConsoleLogger.warning("Please check your database settings in the config.yml file!");
throw e; throw e;
} }
} }
@VisibleForTesting @VisibleForTesting
MySQL(NewSetting settings, HikariDataSource hikariDataSource) { MySQL(Settings settings, HikariDataSource hikariDataSource) {
ds = hikariDataSource;
setParameters(settings);
}
private void setParameters(Settings settings) {
this.host = settings.getProperty(DatabaseSettings.MYSQL_HOST); this.host = settings.getProperty(DatabaseSettings.MYSQL_HOST);
this.port = settings.getProperty(DatabaseSettings.MYSQL_PORT); this.port = settings.getProperty(DatabaseSettings.MYSQL_PORT);
this.username = settings.getProperty(DatabaseSettings.MYSQL_USERNAME); this.username = settings.getProperty(DatabaseSettings.MYSQL_USERNAME);
@ -103,7 +94,6 @@ public class MySQL implements DataSource {
this.phpBbPrefix = settings.getProperty(HooksSettings.PHPBB_TABLE_PREFIX); this.phpBbPrefix = settings.getProperty(HooksSettings.PHPBB_TABLE_PREFIX);
this.phpBbGroup = settings.getProperty(HooksSettings.PHPBB_ACTIVATED_GROUP_ID); this.phpBbGroup = settings.getProperty(HooksSettings.PHPBB_ACTIVATED_GROUP_ID);
this.wordpressPrefix = settings.getProperty(HooksSettings.WORDPRESS_TABLE_PREFIX); this.wordpressPrefix = settings.getProperty(HooksSettings.WORDPRESS_TABLE_PREFIX);
ds = hikariDataSource;
} }
private void setConnectionArguments() throws RuntimeException { private void setConnectionArguments() throws RuntimeException {
@ -147,118 +137,83 @@ public class MySQL implements DataSource {
return ds.getConnection(); return ds.getConnection();
} }
private void setupConnection() throws SQLException { private void checkTablesAndColumns() throws SQLException {
try (Connection con = getConnection()) { try (Connection con = getConnection(); Statement st = con.createStatement()) {
Statement st = con.createStatement(); // Create table with ID column if it doesn't exist
DatabaseMetaData md = con.getMetaData();
// Create table if not exists.
String sql = "CREATE TABLE IF NOT EXISTS " + tableName + " (" String sql = "CREATE TABLE IF NOT EXISTS " + tableName + " ("
+ col.ID + " INTEGER AUTO_INCREMENT," + col.ID + " MEDIUMINT(8) UNSIGNED AUTO_INCREMENT,"
+ col.NAME + " VARCHAR(255) NOT NULL UNIQUE," + "PRIMARY KEY (" + col.ID + ")"
+ col.REAL_NAME + " VARCHAR(255) NOT NULL," + ") CHARACTER SET = utf8;";
+ col.PASSWORD + " VARCHAR(255) NOT NULL,"
+ col.IP + " VARCHAR(40) NOT NULL DEFAULT '127.0.0.1',"
+ col.LAST_LOGIN + " BIGINT NOT NULL DEFAULT 0,"
+ col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0',"
+ col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0',"
+ col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0',"
+ col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT '" + Settings.defaultWorld + "',"
+ col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com',"
+ col.IS_LOGGED + " SMALLINT NOT NULL DEFAULT '0',"
+ "CONSTRAINT table_const_prim PRIMARY KEY (" + col.ID + ")"
+ ");";
st.executeUpdate(sql); st.executeUpdate(sql);
ResultSet rs = md.getColumns(null, null, tableName, col.NAME); DatabaseMetaData md = con.getMetaData();
if (!rs.next()) { if (isColumnMissing(md, col.NAME)) {
st.executeUpdate("ALTER TABLE " + tableName st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.NAME + " VARCHAR(255) NOT NULL UNIQUE AFTER " + col.ID + ";"); + " ADD COLUMN " + col.NAME + " VARCHAR(255) NOT NULL UNIQUE AFTER " + col.ID + ";");
} }
rs.close();
rs = md.getColumns(null, null, tableName, col.REAL_NAME); if (isColumnMissing(md, col.REAL_NAME)) {
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.REAL_NAME + " VARCHAR(255) NOT NULL AFTER " + col.NAME + ";"); + " ADD COLUMN " + col.REAL_NAME + " VARCHAR(255) NOT NULL AFTER " + col.NAME + ";");
} }
rs.close();
rs = md.getColumns(null, null, tableName, col.PASSWORD); if (isColumnMissing(md, col.PASSWORD)) {
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.PASSWORD + " VARCHAR(255) NOT NULL;"); + " ADD COLUMN " + col.PASSWORD + " VARCHAR(255) CHARACTER SET ascii COLLATE ascii_bin NOT NULL;");
}
rs.close();
if (!col.SALT.isEmpty()) {
rs = md.getColumns(null, null, tableName, col.SALT);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.SALT + " VARCHAR(255);");
}
rs.close();
} }
rs = md.getColumns(null, null, tableName, col.IP); if (!col.SALT.isEmpty() && isColumnMissing(md, col.SALT)) {
if (!rs.next()) { st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.SALT + " VARCHAR(255);");
}
if (isColumnMissing(md, col.IP)) {
st.executeUpdate("ALTER TABLE " + tableName st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.IP + " VARCHAR(40) NOT NULL;"); + " ADD COLUMN " + col.IP + " VARCHAR(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL;");
} }
rs.close();
rs = md.getColumns(null, null, tableName, col.LAST_LOGIN); if (isColumnMissing(md, col.LAST_LOGIN)) {
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.LAST_LOGIN + " BIGINT NOT NULL DEFAULT 0;"); + " ADD COLUMN " + col.LAST_LOGIN + " BIGINT NOT NULL DEFAULT 0;");
} else { } else {
migrateLastLoginColumnToBigInt(con, rs); migrateLastLoginColumn(con, md);
} }
rs.close();
rs = md.getColumns(null, null, tableName, col.LASTLOC_X); if (isColumnMissing(md, col.LASTLOC_X)) {
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
+ col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0' AFTER " + col.LAST_LOGIN + " , ADD " + col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0' AFTER " + col.LAST_LOGIN + " , ADD "
+ col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0' AFTER " + col.LASTLOC_X + " , ADD " + col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0' AFTER " + col.LASTLOC_X + " , ADD "
+ col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0' AFTER " + col.LASTLOC_Y); + col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0' AFTER " + col.LASTLOC_Y);
} } else {
rs.close();
rs = md.getColumns(null, null, tableName, col.LASTLOC_X);
if (rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " MODIFY " st.executeUpdate("ALTER TABLE " + tableName + " MODIFY "
+ col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0', MODIFY " + col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0', MODIFY "
+ col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0', MODIFY " + col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0', MODIFY "
+ col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0';"); + col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0';");
} }
rs.close();
rs = md.getColumns(null, null, tableName, col.LASTLOC_WORLD); if (isColumnMissing(md, col.LASTLOC_WORLD)) {
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
+ col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT 'world' AFTER " + col.LASTLOC_Z); + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT 'world' AFTER " + col.LASTLOC_Z);
} }
rs.close();
rs = md.getColumns(null, null, tableName, col.EMAIL); if (isColumnMissing(md, col.EMAIL)) {
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
+ col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com' AFTER " + col.LASTLOC_WORLD); + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com' AFTER " + col.LASTLOC_WORLD);
} }
rs.close();
rs = md.getColumns(null, null, tableName, col.IS_LOGGED); if (isColumnMissing(md, col.IS_LOGGED)) {
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
+ col.IS_LOGGED + " SMALLINT NOT NULL DEFAULT '0' AFTER " + col.EMAIL); + col.IS_LOGGED + " SMALLINT NOT NULL DEFAULT '0' AFTER " + col.EMAIL);
} }
rs.close();
st.close();
} }
ConsoleLogger.info("MySQL setup finished"); ConsoleLogger.info("MySQL setup finished");
} }
private boolean isColumnMissing(DatabaseMetaData metaData, String columnName) throws SQLException {
try (ResultSet rs = metaData.getColumns(null, null, tableName, columnName)) {
return !rs.next();
}
}
@Override @Override
public boolean isAuthAvailable(String user) { public boolean isAuthAvailable(String user) {
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
@ -613,21 +568,18 @@ public class MySQL implements DataSource {
} }
@Override @Override
public Set<String> autoPurgeDatabase(long until) { public Set<String> getRecordsToPurge(long until) {
Set<String> list = new HashSet<>(); Set<String> list = new HashSet<>();
String select = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;"; String select = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;";
String delete = "DELETE FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;";
try (Connection con = getConnection(); try (Connection con = getConnection();
PreparedStatement selectPst = con.prepareStatement(select); PreparedStatement selectPst = con.prepareStatement(select)) {
PreparedStatement deletePst = con.prepareStatement(delete)) {
selectPst.setLong(1, until); selectPst.setLong(1, until);
try (ResultSet rs = selectPst.executeQuery()) { try (ResultSet rs = selectPst.executeQuery()) {
while (rs.next()) { while (rs.next()) {
list.add(rs.getString(col.NAME)); list.add(rs.getString(col.NAME));
} }
} }
deletePst.setLong(1, until);
deletePst.executeUpdate();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -742,11 +694,11 @@ public class MySQL implements DataSource {
} }
@Override @Override
public void purgeBanned(Set<String> banned) { public void purgeRecords(Collection<String> toPurge) {
String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;"; String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
for (String name : banned) { for (String name : toPurge) {
pst.setString(1, name); pst.setString(1, name.toLowerCase());
pst.executeUpdate(); pst.executeUpdate();
} }
} catch (SQLException ex) { } catch (SQLException ex) {
@ -839,20 +791,6 @@ public class MySQL implements DataSource {
return false; return false;
} }
@Override
public boolean updateIp(String user, String ip) {
String sql = "UPDATE " + tableName + " SET " + col.IP + "=? WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, ip);
pst.setString(2, user);
pst.executeUpdate();
return true;
} catch (SQLException ex) {
logSqlException(ex);
}
return false;
}
@Override @Override
public List<PlayerAuth> getAllAuths() { public List<PlayerAuth> getAllAuths() {
List<PlayerAuth> auths = new ArrayList<>(); List<PlayerAuth> auths = new ArrayList<>();
@ -933,40 +871,82 @@ public class MySQL implements DataSource {
} }
/** /**
* Check if the lastlogin column is of type timestamp and, if so, revert it to the bigint format. * Checks if the last login column has a type that needs to be migrated.
* *
* @param con Connection to the database * @param con connection to the database
* @param rs ResultSet containing meta data for the lastlogin column * @param metaData lastlogin column meta data
* @throws SQLException
*/ */
private void migrateLastLoginColumnToBigInt(Connection con, ResultSet rs) throws SQLException { private void migrateLastLoginColumn(Connection con, DatabaseMetaData metaData) throws SQLException {
final int columnType = rs.getInt("DATA_TYPE"); final int columnType;
if (columnType == Types.TIMESTAMP) { try (ResultSet rs = metaData.getColumns(null, null, tableName, col.LAST_LOGIN)) {
ConsoleLogger.info("Migrating lastlogin column from timestamp to bigint"); if (!rs.next()) {
final String lastLoginOld = col.LAST_LOGIN + "_old"; ConsoleLogger.warning("Could not get LAST_LOGIN meta data. This should never happen!");
return;
// Rename lastlogin to lastlogin_old }
String sql = String.format("ALTER TABLE %s CHANGE COLUMN %s %s BIGINT", columnType = rs.getInt("DATA_TYPE");
tableName, col.LAST_LOGIN, lastLoginOld);
PreparedStatement pst = con.prepareStatement(sql);
pst.execute();
// Create lastlogin column
sql = String.format("ALTER TABLE %s ADD COLUMN %s "
+ "BIGINT NOT NULL DEFAULT 0 AFTER %s",
tableName, col.LAST_LOGIN, col.IP);
con.prepareStatement(sql).execute();
// Set values of lastlogin based on lastlogin_old
sql = String.format("UPDATE %s SET %s = UNIX_TIMESTAMP(%s)",
tableName, col.LAST_LOGIN, lastLoginOld);
con.prepareStatement(sql).execute();
// Drop lastlogin_old
sql = String.format("ALTER TABLE %s DROP COLUMN %s",
tableName, lastLoginOld);
con.prepareStatement(sql).execute();
ConsoleLogger.info("Finished migration of lastlogin (timestamp to bigint)");
} }
if (columnType == Types.TIMESTAMP) {
migrateLastLoginColumnFromTimestamp(con);
} else if (columnType == Types.INTEGER) {
migrateLastLoginColumnFromInt(con);
}
}
/**
* Performs conversion of lastlogin column from timestamp type to bigint.
*
* @param con connection to the database
*/
private void migrateLastLoginColumnFromTimestamp(Connection con) throws SQLException {
ConsoleLogger.info("Migrating lastlogin column from timestamp to bigint");
final String lastLoginOld = col.LAST_LOGIN + "_old";
// Rename lastlogin to lastlogin_old
String sql = String.format("ALTER TABLE %s CHANGE COLUMN %s %s BIGINT",
tableName, col.LAST_LOGIN, lastLoginOld);
PreparedStatement pst = con.prepareStatement(sql);
pst.execute();
// Create lastlogin column
sql = String.format("ALTER TABLE %s ADD COLUMN %s "
+ "BIGINT NOT NULL DEFAULT 0 AFTER %s",
tableName, col.LAST_LOGIN, col.IP);
con.prepareStatement(sql).execute();
// Set values of lastlogin based on lastlogin_old
sql = String.format("UPDATE %s SET %s = UNIX_TIMESTAMP(%s) * 1000",
tableName, col.LAST_LOGIN, lastLoginOld);
con.prepareStatement(sql).execute();
// Drop lastlogin_old
sql = String.format("ALTER TABLE %s DROP COLUMN %s",
tableName, lastLoginOld);
con.prepareStatement(sql).execute();
ConsoleLogger.info("Finished migration of lastlogin (timestamp to bigint)");
}
/**
* Performs conversion of lastlogin column from int to bigint.
*
* @param con connection to the database
*/
private void migrateLastLoginColumnFromInt(Connection con) throws SQLException {
// Change from int to bigint
ConsoleLogger.info("Migrating lastlogin column from int to bigint");
String sql = String.format("ALTER TABLE %s MODIFY %s BIGINT;", tableName, col.LAST_LOGIN);
con.prepareStatement(sql).execute();
// Migrate timestamps in seconds format to milliseconds format if they are plausible
int rangeStart = 1262304000; // timestamp for 2010-01-01
int rangeEnd = 1514678400; // timestamp for 2017-12-31
sql = String.format("UPDATE %s SET %s = %s * 1000 WHERE %s > %d AND %s < %d;",
tableName, col.LAST_LOGIN, col.LAST_LOGIN, col.LAST_LOGIN, rangeStart, col.LAST_LOGIN, rangeEnd);
int changedRows = con.prepareStatement(sql).executeUpdate();
ConsoleLogger.warning("You may have entries with invalid timestamps. Please check your data "
+ "before purging. " + changedRows + " rows were migrated from seconds to milliseconds.");
} }
private static void logSqlException(SQLException e) { private static void logSqlException(SQLException e) {

View File

@ -1,26 +1,26 @@
package fr.xephi.authme.datasource; package fr.xephi.authme.datasource;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.util.StringUtils;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.util.StringUtils;
/** /**
*/ */
public class SQLite implements DataSource { public class SQLite implements DataSource {
@ -38,7 +38,7 @@ public class SQLite implements DataSource {
* @throws ClassNotFoundException if no driver could be found for the datasource * @throws ClassNotFoundException if no driver could be found for the datasource
* @throws SQLException when initialization of a SQL datasource failed * @throws SQLException when initialization of a SQL datasource failed
*/ */
public SQLite(NewSetting settings) throws ClassNotFoundException, SQLException { public SQLite(Settings settings) throws ClassNotFoundException, SQLException {
this.database = settings.getProperty(DatabaseSettings.MYSQL_DATABASE); this.database = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
this.col = new Columns(settings); this.col = new Columns(settings);
@ -53,7 +53,7 @@ public class SQLite implements DataSource {
} }
@VisibleForTesting @VisibleForTesting
SQLite(NewSetting settings, Connection connection) { SQLite(Settings settings, Connection connection) {
this.database = settings.getProperty(DatabaseSettings.MYSQL_DATABASE); this.database = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
this.col = new Columns(settings); this.col = new Columns(settings);
@ -70,67 +70,73 @@ public class SQLite implements DataSource {
this.con = DriverManager.getConnection("jdbc:sqlite:plugins/AuthMe/" + database + ".db"); this.con = DriverManager.getConnection("jdbc:sqlite:plugins/AuthMe/" + database + ".db");
} }
private void setup() throws SQLException { @VisibleForTesting
Statement st = null; protected void setup() throws SQLException {
ResultSet rs = null; try (Statement st = con.createStatement()) {
try { // Note: cannot add unique fields later on in SQLite, so we add it on initialization
st = con.createStatement(); st.executeUpdate("CREATE TABLE IF NOT EXISTS " + tableName + " ("
st.executeUpdate("CREATE TABLE IF NOT EXISTS " + tableName + " (" + col.ID + " INTEGER AUTO_INCREMENT," + col.NAME + " VARCHAR(255) NOT NULL UNIQUE," + col.PASSWORD + " VARCHAR(255) NOT NULL," + col.IP + " VARCHAR(40) NOT NULL," + col.LAST_LOGIN + " BIGINT," + col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT '" + Settings.defaultWorld + "'," + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com'," + "CONSTRAINT table_const_prim PRIMARY KEY (" + col.ID + "));"); + col.ID + " INTEGER AUTO_INCREMENT, "
rs = con.getMetaData().getColumns(null, null, tableName, col.PASSWORD); + col.NAME + " VARCHAR(255) NOT NULL UNIQUE, "
if (!rs.next()) { + "CONSTRAINT table_const_prim PRIMARY KEY (" + col.ID + "));");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.PASSWORD + " VARCHAR(255) NOT NULL;");
DatabaseMetaData md = con.getMetaData();
if (isColumnMissing(md, col.REAL_NAME)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
+ col.REAL_NAME + " VARCHAR(255) NOT NULL DEFAULT 'Player';");
} }
rs.close();
if (!col.SALT.isEmpty()) { if (isColumnMissing(md, col.PASSWORD)) {
rs = con.getMetaData().getColumns(null, null, tableName, col.SALT); st.executeUpdate("ALTER TABLE " + tableName
if (!rs.next()) { + " ADD COLUMN " + col.PASSWORD + " VARCHAR(255) NOT NULL DEFAULT '';");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.SALT + " VARCHAR(255);");
}
rs.close();
} }
rs = con.getMetaData().getColumns(null, null, tableName, col.IP);
if (!rs.next()) { if (!col.SALT.isEmpty() && isColumnMissing(md, col.SALT)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.IP + " VARCHAR(40) NOT NULL;"); st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.SALT + " VARCHAR(255);");
} }
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.LAST_LOGIN); if (isColumnMissing(md, col.IP)) {
if (!rs.next()) { st.executeUpdate("ALTER TABLE " + tableName
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LAST_LOGIN + " TIMESTAMP DEFAULT current_timestamp;"); + " ADD COLUMN " + col.IP + " VARCHAR(40) NOT NULL DEFAULT '';");
} }
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.LASTLOC_X); if (isColumnMissing(md, col.LAST_LOGIN)) {
if (!rs.next()) { st.executeUpdate("ALTER TABLE " + tableName
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0';"); + " ADD COLUMN " + col.LAST_LOGIN + " TIMESTAMP;");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0';");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0';");
} }
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.LASTLOC_WORLD); if (isColumnMissing(md, col.LASTLOC_X)) {
if (!rs.next()) { st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_X
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT 'world';"); + " DOUBLE NOT NULL DEFAULT '0.0';");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Y
+ " DOUBLE NOT NULL DEFAULT '0.0';");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Z
+ " DOUBLE NOT NULL DEFAULT '0.0';");
} }
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.EMAIL); if (isColumnMissing(md, col.LASTLOC_WORLD)) {
if (!rs.next()) { st.executeUpdate("ALTER TABLE " + tableName
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com';"); + " ADD COLUMN " + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT 'world';");
} }
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.IS_LOGGED); if (isColumnMissing(md, col.EMAIL)) {
if (!rs.next()) { st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com';");
}
if (isColumnMissing(md, col.IS_LOGGED)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.IS_LOGGED + " INT DEFAULT '0';"); st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.IS_LOGGED + " INT DEFAULT '0';");
} }
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.REAL_NAME);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.REAL_NAME + " VARCHAR(255) NOT NULL DEFAULT 'Player';");
}
} finally {
close(rs);
close(st);
} }
ConsoleLogger.info("SQLite Setup finished"); ConsoleLogger.info("SQLite Setup finished");
} }
private boolean isColumnMissing(DatabaseMetaData metaData, String columnName) throws SQLException {
try (ResultSet rs = metaData.getColumns(null, null, tableName, columnName)) {
return !rs.next();
}
}
@Override @Override
public void reload() { public void reload() {
close(con); close(con);
@ -147,12 +153,12 @@ public class SQLite implements DataSource {
PreparedStatement pst = null; PreparedStatement pst = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=LOWER(?);"); pst = con.prepareStatement("SELECT 1 FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=LOWER(?);");
pst.setString(1, user); pst.setString(1, user);
rs = pst.executeQuery(); rs = pst.executeQuery();
return rs.next(); return rs.next();
} catch (SQLException ex) { } catch (SQLException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return false; return false;
} finally { } finally {
close(rs); close(rs);
@ -207,7 +213,7 @@ public class SQLite implements DataSource {
HashedPassword password = auth.getPassword(); HashedPassword password = auth.getPassword();
if (col.SALT.isEmpty()) { if (col.SALT.isEmpty()) {
if (!StringUtils.isEmpty(auth.getPassword().getSalt())) { if (!StringUtils.isEmpty(auth.getPassword().getSalt())) {
ConsoleLogger.showError("Warning! Detected hashed password with separate salt but the salt column " ConsoleLogger.warning("Warning! Detected hashed password with separate salt but the salt column "
+ "is not set in the config!"); + "is not set in the config!");
} }
pst = con.prepareStatement("INSERT INTO " + tableName + "(" + col.NAME + "," + col.PASSWORD + pst = con.prepareStatement("INSERT INTO " + tableName + "(" + col.NAME + "," + col.PASSWORD +
@ -293,20 +299,17 @@ public class SQLite implements DataSource {
} }
@Override @Override
public Set<String> autoPurgeDatabase(long until) { public Set<String> getRecordsToPurge(long until) {
Set<String> list = new HashSet<>(); Set<String> list = new HashSet<>();
String select = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;"; String select = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;";
String delete = "DELETE FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;"; try (PreparedStatement selectPst = con.prepareStatement(select)) {
try (PreparedStatement selectPst = con.prepareStatement(select);
PreparedStatement deletePst = con.prepareStatement(delete)) {
selectPst.setLong(1, until); selectPst.setLong(1, until);
try (ResultSet rs = selectPst.executeQuery()) { try (ResultSet rs = selectPst.executeQuery()) {
while (rs.next()) { while (rs.next()) {
list.add(rs.getString(col.NAME)); list.add(rs.getString(col.NAME));
} }
} }
deletePst.setLong(1, until);
deletePst.executeUpdate();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -314,6 +317,19 @@ public class SQLite implements DataSource {
return list; return list;
} }
@Override
public void purgeRecords(Collection<String> toPurge) {
String delete = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;";
try (PreparedStatement deletePst = con.prepareStatement(delete)) {
for (String name : toPurge) {
deletePst.setString(1, name.toLowerCase());
deletePst.executeUpdate();
}
} catch (SQLException ex) {
logSqlException(ex);
}
}
@Override @Override
public boolean removeAuth(String user) { public boolean removeAuth(String user) {
PreparedStatement pst = null; PreparedStatement pst = null;
@ -443,19 +459,6 @@ public class SQLite implements DataSource {
return 0; return 0;
} }
@Override
public void purgeBanned(Set<String> banned) {
String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;";
try (PreparedStatement pst = con.prepareStatement(sql)) {
for (String name : banned) {
pst.setString(1, name);
pst.executeUpdate();
}
} catch (SQLException ex) {
logSqlException(ex);
}
}
@Override @Override
public DataSourceType getType() { public DataSourceType getType() {
return DataSourceType.SQLITE; return DataSourceType.SQLITE;
@ -553,20 +556,6 @@ public class SQLite implements DataSource {
return false; return false;
} }
@Override
public boolean updateIp(String user, String ip) {
String sql = "UPDATE " + tableName + " SET " + col.IP + "=? WHERE " + col.NAME + "=?;";
try (PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, ip);
pst.setString(2, user);
pst.executeUpdate();
return true;
} catch (SQLException ex) {
logSqlException(ex);
}
return false;
}
@Override @Override
public List<PlayerAuth> getAllAuths() { public List<PlayerAuth> getAllAuths() {
List<PlayerAuth> auths = new ArrayList<>(); List<PlayerAuth> auths = new ArrayList<>();

View File

@ -2,21 +2,17 @@ package fr.xephi.authme.hooks;
import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.BukkitService;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener; import org.bukkit.plugin.messaging.PluginMessageListener;
import javax.inject.Inject; import javax.inject.Inject;
public class BungeeCordMessage implements PluginMessageListener { public class BungeeCordMessage implements PluginMessageListener {
@Inject @Inject
@ -28,9 +24,6 @@ public class BungeeCordMessage implements PluginMessageListener {
@Inject @Inject
private PlayerCache playerCache; private PlayerCache playerCache;
@Inject
private AuthMe plugin;
BungeeCordMessage() { } BungeeCordMessage() { }
@ -56,26 +49,13 @@ public class BungeeCordMessage implements PluginMessageListener {
if ("login".equals(act)) { if ("login".equals(act)) {
playerCache.updatePlayer(auth); playerCache.updatePlayer(auth);
dataSource.setLogged(name); dataSource.setLogged(name);
//START 03062016 sgdc3: should fix #731 but we need to recode this mess ConsoleLogger.fine("Player " + auth.getNickname() + " has logged in from one of your server!");
if (plugin.sessions.containsKey(name)) {
plugin.sessions.get(name).cancel();
plugin.sessions.remove(name);
}
//END
if (!plugin.getSettings().getProperty(SecuritySettings.REMOVE_SPAM_FROM_CONSOLE)) {
ConsoleLogger.info("Player " + auth.getNickname() + " has logged in from one of your server!");
}
} else if ("logout".equals(act)) { } else if ("logout".equals(act)) {
playerCache.removePlayer(name); playerCache.removePlayer(name);
dataSource.setUnlogged(name); dataSource.setUnlogged(name);
if (!plugin.getSettings().getProperty(SecuritySettings.REMOVE_SPAM_FROM_CONSOLE)) { ConsoleLogger.fine("Player " + auth.getNickname() + " has logged out from one of your server!");
ConsoleLogger.info("Player " + auth.getNickname() + " has logged out from one of your server!");
}
} else if ("register".equals(act)) { } else if ("register".equals(act)) {
if (!plugin.getSettings().getProperty(SecuritySettings.REMOVE_SPAM_FROM_CONSOLE)) { ConsoleLogger.fine("Player " + auth.getNickname() + " has registered out from one of your server!");
ConsoleLogger.info("Player " + auth.getNickname() + " has registered out from one of your server!");
}
} else if ("changepassword".equals(act)) { } else if ("changepassword".equals(act)) {
final String password = args[2]; final String password = args[2];
final String salt = args.length >= 4 ? args[3] : null; final String salt = args.length >= 4 ? args[3] : null;

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.hooks; package fr.xephi.authme.hooks;
import ch.jalu.injector.annotations.NoFieldScan;
import com.earth2me.essentials.Essentials; import com.earth2me.essentials.Essentials;
import com.onarandombox.MultiverseCore.MultiverseCore; import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager; import com.onarandombox.MultiverseCore.api.MVWorldManager;
@ -17,6 +18,7 @@ import java.io.File;
/** /**
* Hooks into third-party plugins and allows to perform actions on them. * Hooks into third-party plugins and allows to perform actions on them.
*/ */
@NoFieldScan
public class PluginHooks { public class PluginHooks {
private final PluginManager pluginManager; private final PluginManager pluginManager;

View File

@ -1,324 +0,0 @@
package fr.xephi.authme.initialization;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import fr.xephi.authme.settings.NewSetting;
import javax.annotation.PostConstruct;
import javax.inject.Provider;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Dependency injector of AuthMe: initializes and injects services and tasks.
* <p>
* Only constructor and field injection are supported, indicated with the JSR330
* {@link javax.inject.Inject @Inject} annotation.
* <p>
* {@link PostConstruct @PostConstruct} methods are recognized and invoked upon
* instantiation. Note that the parent classes are <i>not</i> scanned for such methods.
*/
public class AuthMeServiceInitializer {
private final Set<String> ALLOWED_PACKAGES;
private final Map<Class<?>, Object> objects;
/**
* Constructor.
*
* @param allowedPackages list of allowed packages. Only classes whose package
* starts with any of the given entries will be instantiated
*/
public AuthMeServiceInitializer(String... allowedPackages) {
ALLOWED_PACKAGES = ImmutableSet.copyOf(allowedPackages);
objects = new HashMap<>();
objects.put(getClass(), this);
}
/**
* Retrieves or instantiates an object of the given type.
*
* @param clazz the class to retrieve the value for
* @param <T> the class' type
* @return object of the class' type
*/
public <T> T get(Class<T> clazz) {
return get(clazz, new HashSet<Class<?>>());
}
/**
* Register an object with a custom class (supertype). Use this for example to specify a
* concrete implementation of an interface or an abstract class.
*
* @param clazz the class to register the object for
* @param object the object
* @param <T> the class' type
*/
public <T> void register(Class<? super T> clazz, T object) {
if (objects.containsKey(clazz)) {
throw new IllegalStateException("There is already an object present for " + clazz);
}
Preconditions.checkNotNull(object);
objects.put(clazz, object);
}
/**
* Associate an annotation with a value.
*
* @param annotation the annotation
* @param value the value
*/
public void provide(Class<? extends Annotation> annotation, Object value) {
if (objects.containsKey(annotation)) {
throw new IllegalStateException("Annotation @" + annotation.getClass().getSimpleName()
+ " already registered");
}
Preconditions.checkNotNull(value);
objects.put(annotation, value);
}
/**
* Creates a new instance of the given class and does <i>not</i> keep track of it afterwards,
* unlike {@link #get(Class)} (singleton scope).
*
* @param clazz the class to instantiate
* @param <T> the class' type
* @return new instance of class T
*/
public <T> T newInstance(Class<T> clazz) {
return instantiate(clazz, new HashSet<Class<?>>());
}
/**
* Returns an instance of the given class if available. This simply returns the instance if present and
* otherwise {@code null}. Calling this method will not instantiate anything.
*
* @param clazz the class to retrieve the instance for
* @param <T> the class' type
* @return instance or null if none available
*/
public <T> T getIfAvailable(Class<T> clazz) {
if (Annotation.class.isAssignableFrom(clazz)) {
throw new UnsupportedOperationException("Annotations may not be retrieved in this way!");
}
return clazz.cast(objects.get(clazz));
}
/**
* Returns an instance of the given class by retrieving it or by instantiating it if not yet present.
*
* @param clazz the class to retrieve a value for
* @param traversedClasses the list of traversed classes
* @param <T> the class' type
* @return instance or associated value (for annotations)
*/
private <T> T get(Class<T> clazz, Set<Class<?>> traversedClasses) {
if (Annotation.class.isAssignableFrom(clazz)) {
throw new UnsupportedOperationException("Cannot retrieve annotated elements in this way!");
} else if (objects.containsKey(clazz)) {
return clazz.cast(objects.get(clazz));
}
// First time we come across clazz, need to instantiate it. Validate that we can do so
validatePackage(clazz);
validateInstantiable(clazz);
// Add the clazz to the list of traversed classes in a new Set, so each path we take has its own Set.
traversedClasses = new HashSet<>(traversedClasses);
traversedClasses.add(clazz);
T object = instantiate(clazz, traversedClasses);
storeObject(object);
return object;
}
/**
* Performs a reload on all applicable instances which are registered.
* Requires that the {@link NewSetting settings} instance be registered.
* <p>
* Note that the order in which these classes are reloaded is not guaranteed.
*/
public void performReloadOnServices() {
NewSetting settings = (NewSetting) objects.get(NewSetting.class);
if (settings == null) {
throw new IllegalStateException("Settings instance is null");
}
for (Object object : objects.values()) {
if (object instanceof Reloadable) {
((Reloadable) object).reload();
} else if (object instanceof SettingsDependent) {
((SettingsDependent) object).loadSettings(settings);
}
}
}
/**
* Instantiates the given class by locating its @Inject elements and retrieving
* or instantiating the required instances.
*
* @param clazz the class to instantiate
* @param traversedClasses collection of classes already traversed
* @param <T> the class' type
* @return the instantiated object
*/
private <T> T instantiate(Class<T> clazz, Set<Class<?>> traversedClasses) {
Injection<T> injection = firstNotNull(
ConstructorInjection.provide(clazz), FieldInjection.provide(clazz), InstantiationFallback.provide(clazz));
if (injection == null) {
throw new IllegalStateException("Did not find injection method for " + clazz + ". Make sure you have "
+ "a constructor with @Inject or fields with @Inject. Fields with @Inject require "
+ "the default constructor");
}
validateInjectionHasNoCircularDependencies(injection.getDependencies(), traversedClasses);
Object[] dependencies = resolveDependencies(injection, traversedClasses);
T object = injection.instantiateWith(dependencies);
executePostConstructMethod(object);
return object;
}
/**
* Resolves the dependencies for the given class instantiation, i.e. returns a collection that satisfy
* the class' dependencies by retrieving elements or instantiating them where necessary.
*
* @param injection the injection parameters
* @param traversedClasses collection of traversed classes
* @return array with the parameters to use in the constructor
*/
private Object[] resolveDependencies(Injection<?> injection, Set<Class<?>> traversedClasses) {
Class<?>[] dependencies = injection.getDependencies();
Class<?>[] annotations = injection.getDependencyAnnotations();
Object[] values = new Object[dependencies.length];
for (int i = 0; i < dependencies.length; ++i) {
if (annotations[i] == null) {
values[i] = get(dependencies[i], traversedClasses);
} else {
Object value = objects.get(annotations[i]);
if (value == null) {
throw new IllegalStateException("Value for field with @" + annotations[i].getSimpleName()
+ " must be registered beforehand");
}
values[i] = value;
}
}
return values;
}
/**
* Stores the given object with its class as key. Throws an exception if the key already has
* a value associated to it.
*
* @param object the object to store
*/
private void storeObject(Object object) {
if (objects.containsKey(object.getClass())) {
throw new IllegalStateException("There is already an object present for " + object.getClass());
}
Preconditions.checkNotNull(object);
objects.put(object.getClass(), object);
}
/**
* Validates that none of the dependencies' types are present in the given collection
* of traversed classes. This prevents circular dependencies.
*
* @param dependencies the dependencies of the class
* @param traversedClasses the collection of traversed classes
*/
private static void validateInjectionHasNoCircularDependencies(Class<?>[] dependencies,
Set<Class<?>> traversedClasses) {
for (Class<?> clazz : dependencies) {
if (traversedClasses.contains(clazz)) {
throw new IllegalStateException("Found cyclic dependency - already traversed '" + clazz
+ "' (full traversal list: " + traversedClasses + ")");
}
}
}
/**
* Validates the package of a parameter type to ensure that it is part of the allowed packages.
* This ensures that we don't try to instantiate things that are beyond our reach in case some
* external parameter type has not been registered.
*
* @param clazz the class to validate
*/
private void validatePackage(Class<?> clazz) {
if (clazz.getPackage() == null) {
throw new IllegalStateException("Primitive types must be provided explicitly (or use an annotation).");
}
String packageName = clazz.getPackage().getName();
for (String allowedPackage : ALLOWED_PACKAGES) {
if (packageName.startsWith(allowedPackage)) {
return;
}
}
throw new IllegalStateException("Class " + clazz + " with package " + packageName + " is outside of the "
+ "allowed packages. It must be provided explicitly or the package must be passed to the constructor.");
}
/**
* Executes an object's method annotated with {@link PostConstruct} if present.
* Throws an exception if there are multiple such methods, or if the method is static.
*
* @param object the object to execute the post construct method for
*/
private static void executePostConstructMethod(Object object) {
Method postConstructMethod = getAndValidatePostConstructMethod(object.getClass());
if (postConstructMethod != null) {
try {
postConstructMethod.setAccessible(true);
postConstructMethod.invoke(object);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new UnsupportedOperationException("Error executing @PostConstruct method", e);
}
}
}
private static void validateInstantiable(Class<?> clazz) {
if (clazz.isEnum() || clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) {
throw new IllegalStateException("Class " + clazz.getSimpleName() + " cannot be instantiated");
}
}
/**
* Validate and locate the given class' post construct method. Returns {@code null} if none present.
*
* @param clazz the class to search
* @return post construct method, or null
*/
private static Method getAndValidatePostConstructMethod(Class<?> clazz) {
Method postConstructMethod = null;
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(PostConstruct.class)) {
if (postConstructMethod != null) {
throw new IllegalStateException("Multiple methods with @PostConstruct on " + clazz);
} else if (method.getParameterTypes().length > 0 || Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@PostConstruct method may not be static or have any parameters. "
+ "Invalid method in " + clazz);
} else if (method.getReturnType() != void.class) {
throw new IllegalStateException("@PostConstruct method must have return type void. "
+ "Offending class: " + clazz);
} else {
postConstructMethod = method;
}
}
}
return postConstructMethod;
}
@SafeVarargs
private static <T> Injection<T> firstNotNull(Provider<? extends Injection<T>>... providers) {
for (Provider<? extends Injection<T>> provider : providers) {
Injection<T> object = provider.get();
if (object != null) {
return object;
}
}
return null;
}
}

View File

@ -1,86 +0,0 @@
package fr.xephi.authme.initialization;
import com.google.common.base.Preconditions;
import javax.inject.Inject;
import javax.inject.Provider;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* Functionality for constructor injection.
*/
public class ConstructorInjection<T> implements Injection<T> {
private final Constructor<T> constructor;
private ConstructorInjection(Constructor<T> constructor) {
this.constructor = constructor;
}
@Override
public Class<?>[] getDependencies() {
return constructor.getParameterTypes();
}
@Override
public Class<?>[] getDependencyAnnotations() {
Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
Class<?>[] annotations = new Class<?>[parameterAnnotations.length];
for (int i = 0; i < parameterAnnotations.length; ++i) {
annotations[i] = parameterAnnotations[i].length > 0
? parameterAnnotations[i][0].annotationType()
: null;
}
return annotations;
}
@Override
public T instantiateWith(Object... values) {
validateNoNullValues(values);
try {
return constructor.newInstance(values);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new UnsupportedOperationException(e);
}
}
public static <T> Provider<ConstructorInjection<T>> provide(final Class<T> clazz) {
return new Provider<ConstructorInjection<T>>() {
@Override
public ConstructorInjection<T> get() {
Constructor<T> constructor = getInjectionConstructor(clazz);
return constructor == null ? null : new ConstructorInjection<>(constructor);
}
};
}
/**
* Gets the first found constructor annotated with {@link Inject} of the given class
* and marks it as accessible.
*
* @param clazz the class to process
* @param <T> the class' type
* @return injection constructor for the class, null if not applicable
*/
@SuppressWarnings("unchecked")
private static <T> Constructor<T> getInjectionConstructor(Class<T> clazz) {
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
if (constructor.isAnnotationPresent(Inject.class)) {
constructor.setAccessible(true);
return (Constructor<T>) constructor;
}
}
return null;
}
private static void validateNoNullValues(Object[] array) {
for (Object entry : array) {
Preconditions.checkNotNull(entry);
}
}
}

View File

@ -1,128 +0,0 @@
package fr.xephi.authme.initialization;
import com.google.common.base.Preconditions;
import javax.inject.Inject;
import javax.inject.Provider;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Functionality for field injection.
*/
public class FieldInjection<T> implements Injection<T> {
private final Field[] fields;
private final Constructor<T> defaultConstructor;
private FieldInjection(Constructor<T> defaultConstructor, Collection<Field> fields) {
this.fields = fields.toArray(new Field[fields.size()]);
this.defaultConstructor = defaultConstructor;
}
@Override
public Class<?>[] getDependencies() {
Class<?>[] types = new Class<?>[fields.length];
for (int i = 0; i < fields.length; ++i) {
types[i] = fields[i].getType();
}
return types;
}
@Override
public Class<?>[] getDependencyAnnotations() {
Class<?>[] annotations = new Class<?>[fields.length];
for (int i = 0; i < fields.length; ++i) {
annotations[i] = getFirstNonInjectAnnotation(fields[i]);
}
return annotations;
}
@Override
public T instantiateWith(Object... values) {
Preconditions.checkArgument(values.length == fields.length,
"The number of values must be equal to the number of fields");
T instance;
try {
instance = defaultConstructor.newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new UnsupportedOperationException(e);
}
for (int i = 0; i < fields.length; ++i) {
try {
Preconditions.checkNotNull(values[i]);
fields[i].set(instance, values[i]);
} catch (IllegalAccessException e) {
throw new UnsupportedOperationException(e);
}
}
return instance;
}
/**
* Returns a provider for a {@code FieldInjection<T>} instance, i.e. a provides an object
* with which field injection can be performed on the given class if applicable. The provided
* value is {@code null} if field injection cannot be applied to the class.
*
* @param clazz the class to provide field injection for
* @param <T> the class' type
* @return field injection provider for the given class, or null if not applicable
*/
public static <T> Provider<FieldInjection<T>> provide(final Class<T> clazz) {
return new Provider<FieldInjection<T>>() {
@Override
public FieldInjection<T> get() {
Constructor<T> constructor = getDefaultConstructor(clazz);
if (constructor == null) {
return null;
}
List<Field> fields = getInjectionFields(clazz);
return fields.isEmpty() ? null : new FieldInjection<>(constructor, fields);
}
};
}
private static List<Field> getInjectionFields(Class<?> clazz) {
List<Field> fields = new ArrayList<>();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException(String.format("Field '%s' in class '%s' is static but "
+ "annotated with @Inject", field.getName(), clazz.getSimpleName()));
}
field.setAccessible(true);
fields.add(field);
}
}
return fields;
}
private static Class<?> getFirstNonInjectAnnotation(Field field) {
for (Annotation annotation : field.getAnnotations()) {
if (annotation.annotationType() != Inject.class) {
return annotation.annotationType();
}
}
return null;
}
@SuppressWarnings("unchecked")
private static <T> Constructor<T> getDefaultConstructor(Class<T> clazz) {
try {
Constructor<?> defaultConstructor = clazz.getDeclaredConstructor();
defaultConstructor.setAccessible(true);
return (Constructor<T>) defaultConstructor;
} catch (NoSuchMethodException ignore) {
// no default constructor available
}
return null;
}
}

View File

@ -0,0 +1,14 @@
package fr.xephi.authme.initialization;
/**
* Common interface for types which have data that becomes outdated
* and that can be cleaned up periodically.
*/
public interface HasCleanup {
/**
* Performs the cleanup action.
*/
void performCleanup();
}

View File

@ -0,0 +1,171 @@
package fr.xephi.authme.initialization;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.CacheDataSource;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.datasource.FlatFile;
import fr.xephi.authme.datasource.MySQL;
import fr.xephi.authme.datasource.SQLite;
import fr.xephi.authme.output.ConsoleFilter;
import fr.xephi.authme.output.Log4JFilter;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SettingsMigrationService;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.settings.properties.SettingsFieldRetriever;
import fr.xephi.authme.settings.propertymap.PropertyMap;
import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.FileUtils;
import fr.xephi.authme.util.MigrationService;
import fr.xephi.authme.util.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.logging.Logger;
import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS;
import static fr.xephi.authme.util.BukkitService.TICKS_PER_MINUTE;
/**
* Initializes various services.
*/
public class Initializer {
private static final String FLATFILE_FILENAME = "auths.db";
private static final int SQLITE_MAX_SIZE = 4000;
private AuthMe authMe;
private BukkitService bukkitService;
public Initializer(AuthMe authMe, BukkitService bukkitService) {
this.authMe = authMe;
this.bukkitService = bukkitService;
}
/**
* Loads the plugin's settings.
*
* @return The settings instance, or null if it could not be constructed
*/
public Settings createSettings() throws Exception {
File configFile = new File(authMe.getDataFolder(), "config.yml");
PropertyMap properties = SettingsFieldRetriever.getAllPropertyFields();
SettingsMigrationService migrationService = new SettingsMigrationService();
if (FileUtils.copyFileFromResource(configFile, "config.yml")) {
return new Settings(configFile, authMe.getDataFolder(), properties, migrationService);
}
throw new Exception("Could not copy config.yml from JAR to plugin folder");
}
/**
* Sets up the data source.
*
* @param settings the settings
* @throws ClassNotFoundException if no driver could be found for the datasource
* @throws SQLException when initialization of a SQL datasource failed
* @throws IOException if flat file cannot be read
*/
public DataSource setupDatabase(Settings settings) throws ClassNotFoundException, SQLException, IOException {
DataSourceType dataSourceType = settings.getProperty(DatabaseSettings.BACKEND);
DataSource dataSource;
switch (dataSourceType) {
case FILE:
File source = new File(authMe.getDataFolder(), FLATFILE_FILENAME);
dataSource = new FlatFile(source);
break;
case MYSQL:
dataSource = new MySQL(settings);
break;
case SQLITE:
dataSource = new SQLite(settings);
break;
default:
throw new UnsupportedOperationException("Unknown data source type '" + dataSourceType + "'");
}
DataSource convertedSource = MigrationService.convertFlatfileToSqlite(settings, dataSource);
dataSource = convertedSource == null ? dataSource : convertedSource;
if (settings.getProperty(DatabaseSettings.USE_CACHING)) {
dataSource = new CacheDataSource(dataSource);
}
if (DataSourceType.SQLITE.equals(dataSourceType)) {
checkDataSourceSize(dataSource);
}
return dataSource;
}
private void checkDataSourceSize(final DataSource dataSource) {
bukkitService.runTaskAsynchronously(new Runnable() {
@Override
public void run() {
int accounts = dataSource.getAccountsRegistered();
if (accounts >= SQLITE_MAX_SIZE) {
ConsoleLogger.warning("YOU'RE USING THE SQLITE DATABASE WITH "
+ accounts + "+ ACCOUNTS; FOR BETTER PERFORMANCE, PLEASE UPGRADE TO MYSQL!!");
}
}
});
}
/**
* Sets up the console filter if enabled.
*
* @param settings the settings
* @param logger the plugin logger
*/
public void setupConsoleFilter(Settings settings, Logger logger) {
if (!settings.getProperty(SecuritySettings.REMOVE_PASSWORD_FROM_CONSOLE)) {
return;
}
// Try to set the log4j filter
try {
Class.forName("org.apache.logging.log4j.core.filter.AbstractFilter");
setLog4JFilter();
} catch (ClassNotFoundException | NoClassDefFoundError e) {
// log4j is not available
ConsoleLogger.info("You're using Minecraft 1.6.x or older, Log4J support will be disabled");
ConsoleFilter filter = new ConsoleFilter();
logger.setFilter(filter);
Bukkit.getLogger().setFilter(filter);
Logger.getLogger("Minecraft").setFilter(filter);
}
}
// Set the console filter to remove the passwords
private static void setLog4JFilter() {
org.apache.logging.log4j.core.Logger logger;
logger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger();
logger.addFilter(new Log4JFilter());
}
public void scheduleRecallEmailTask(Settings settings, final DataSource dataSource, final Messages messages) {
if (!settings.getProperty(RECALL_PLAYERS)) {
return;
}
bukkitService.runTaskTimerAsynchronously(new Runnable() {
@Override
public void run() {
for (PlayerAuth auth : dataSource.getLoggedPlayers()) {
String email = auth.getEmail();
if (StringUtils.isEmpty(email) || "your@email.com".equalsIgnoreCase(email)) {
Player player = bukkitService.getPlayerExact(auth.getRealName());
if (player != null) {
messages.send(player, MessageKey.ADD_EMAIL_MESSAGE);
}
}
}
}
}, 1, TICKS_PER_MINUTE * settings.getProperty(EmailSettings.DELAY_RECALL));
}
}

View File

@ -1,36 +0,0 @@
package fr.xephi.authme.initialization;
/**
* Common interface for all injection methods.
*
* @param <T> the type of the concerned object
*/
public interface Injection<T> {
/**
* Returns the dependencies that must be provided to instantiate the given item.
*
* @return list of dependencies
* @see #instantiateWith
*/
Class<?>[] getDependencies();
/**
* Returns the annotation on each dependency if available. The indices of this
* array correspond to the ones of {@link #getDependencies()}. If no annotation
* is available, {@code null} is stored. If multiple annotations are present, only
* one is stored (no guarantee on which one).
*
* @return annotation for each dependency
*/
Class<?>[] getDependencyAnnotations();
/**
* Creates a new instance with the given values as dependencies. The given values
* must correspond to {@link #getDependencies()} in size, order and type.
*
* @param values the values to set for the dependencies
* @return resulting object
*/
T instantiateWith(Object... values);
}

View File

@ -1,85 +0,0 @@
package fr.xephi.authme.initialization;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Provider;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* Fallback instantiation method for classes with an accessible no-args constructor
* and no elements whatsoever annotated with {@link Inject} or {@link PostConstruct}.
*/
public class InstantiationFallback<T> implements Injection<T> {
private final Constructor<T> constructor;
private InstantiationFallback(Constructor<T> constructor) {
this.constructor = constructor;
}
@Override
public Class<?>[] getDependencies() {
return new Class<?>[0];
}
@Override
public Class<?>[] getDependencyAnnotations() {
return new Class<?>[0];
}
@Override
public T instantiateWith(Object... values) {
if (values == null || values.length > 0) {
throw new UnsupportedOperationException("Instantiation fallback cannot have parameters");
}
try {
return constructor.newInstance();
} catch (InvocationTargetException | IllegalAccessException | InstantiationException e) {
throw new UnsupportedOperationException(e);
}
}
/**
* Returns an instantiation fallback if the class is applicable.
*
* @param clazz the class
* @param <T> the class' type
* @return instantiation fallback provider for the given class, or null if not applicable
*/
public static <T> Provider<InstantiationFallback<T>> provide(final Class<T> clazz) {
return new Provider<InstantiationFallback<T>>() {
@Override
public InstantiationFallback<T> get() {
Constructor<T> noArgsConstructor = getNoArgsConstructor(clazz);
// Return fallback only if we have no args constructor and no @Inject annotation anywhere
if (noArgsConstructor != null
&& !isInjectionAnnotationPresent(clazz.getDeclaredConstructors())
&& !isInjectionAnnotationPresent(clazz.getDeclaredFields())
&& !isInjectionAnnotationPresent(clazz.getDeclaredMethods())) {
return new InstantiationFallback<>(noArgsConstructor);
}
return null;
}
};
}
private static <T> Constructor<T> getNoArgsConstructor(Class<T> clazz) {
try {
// Note ljacqu 20160504: getConstructor(), unlike getDeclaredConstructor(), only considers public members
return clazz.getConstructor();
} catch (NoSuchMethodException e) {
return null;
}
}
private static <A extends AccessibleObject> boolean isInjectionAnnotationPresent(A[] accessibles) {
for (A accessible : accessibles) {
if (accessible.isAnnotationPresent(Inject.class) || accessible.isAnnotationPresent(PostConstruct.class)) {
return true;
}
}
return false;
}
}

View File

@ -2,7 +2,7 @@ package fr.xephi.authme.initialization;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.PluginSettings;
import org.mcstats.Metrics; import org.mcstats.Metrics;
@ -10,12 +10,12 @@ import org.mcstats.Metrics.Graph;
import java.io.IOException; import java.io.IOException;
public class MetricsStarter { public class MetricsManager {
private MetricsStarter() { private MetricsManager() {
} }
public static void setupMetrics(AuthMe plugin, NewSetting settings) { public static void sendMetrics(AuthMe plugin, Settings settings) {
try { try {
final Metrics metrics = new Metrics(plugin); final Metrics metrics = new Metrics(plugin);
@ -41,7 +41,7 @@ public class MetricsStarter {
metrics.start(); metrics.start();
} catch (IOException e) { } catch (IOException e) {
// Failed to submit the metrics data // Failed to submit the metrics data
ConsoleLogger.logException("Can't start Metrics! The plugin will work anyway...", e); ConsoleLogger.logException("Can't send Metrics data! The plugin will work anyway...", e);
} }
} }
} }

View File

@ -0,0 +1,85 @@
package fr.xephi.authme.initialization;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.cache.backup.PlayerDataStorage;
import fr.xephi.authme.cache.limbo.LimboCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.hooks.PluginHooks;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.ValidationService;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import javax.inject.Inject;
/**
* Saves all players' data when the plugin shuts down.
*/
public class OnShutdownPlayerSaver {
@Inject
private BukkitService bukkitService;
@Inject
private Settings settings;
@Inject
private ValidationService validationService;
@Inject
private LimboCache limboCache;
@Inject
private DataSource dataSource;
@Inject
private PlayerDataStorage playerDataStorage;
@Inject
private SpawnLoader spawnLoader;
@Inject
private PluginHooks pluginHooks;
@Inject
private PlayerCache playerCache;
OnShutdownPlayerSaver() {
}
/**
* Saves the data of all online players.
*/
public void saveAllPlayers() {
for (Player player : bukkitService.getOnlinePlayers()) {
savePlayer(player);
}
}
private void savePlayer(Player player) {
final String name = player.getName().toLowerCase();
if (pluginHooks.isNpc(player) || validationService.isUnrestricted(name)) {
return;
}
if (limboCache.hasPlayerData(name)) {
limboCache.restoreData(player);
limboCache.removeFromCache(player);
} else {
saveLoggedinPlayer(player);
}
playerCache.removePlayer(name);
}
private void saveLoggedinPlayer(Player player) {
if (settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)) {
Location loc = spawnLoader.getPlayerLocationOrSpawn(player);
final PlayerAuth auth = PlayerAuth.builder()
.name(player.getName().toLowerCase())
.realName(player.getName())
.location(loc).build();
dataSource.updateQuitLoc(auth);
}
if (settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)
&& !settings.getProperty(RestrictionSettings.NO_TELEPORT)) {
if (!playerDataStorage.hasData(player)) {
playerDataStorage.saveData(player);
}
}
}
}

View File

@ -1,6 +1,6 @@
package fr.xephi.authme.initialization; package fr.xephi.authme.initialization;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
/** /**
* Interface for classes that keep a local copy of certain settings. * Interface for classes that keep a local copy of certain settings.
@ -8,9 +8,9 @@ import fr.xephi.authme.settings.NewSetting;
public interface SettingsDependent { public interface SettingsDependent {
/** /**
* Loads the needed settings. * Performs a reload with the provided settings instance.
* *
* @param settings the settings instance * @param settings the settings instance
*/ */
void loadSettings(NewSetting settings); void reload(Settings settings);
} }

View File

@ -0,0 +1,88 @@
package fr.xephi.authme.initialization;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.datasource.DataSource;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitWorker;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Waits for asynchronous tasks to complete before closing the data source
* so the plugin can shut down properly.
*/
public class TaskCloser implements Runnable {
private final BukkitScheduler scheduler;
private final Logger logger;
private final AuthMe plugin;
private final DataSource dataSource;
/**
* Constructor.
*
* @param plugin the plugin instance
* @param dataSource the data source (nullable)
*/
public TaskCloser(AuthMe plugin, DataSource dataSource) {
this.scheduler = plugin.getServer().getScheduler();
this.logger = plugin.getLogger();
this.plugin = plugin;
this.dataSource = dataSource;
}
@Override
public void run() {
List<Integer> pendingTasks = getPendingTasks();
logger.log(Level.INFO, "Waiting for {0} tasks to finish", pendingTasks.size());
int progress = 0;
//one minute + some time checking the running state
int tries = 60;
while (!pendingTasks.isEmpty()) {
if (tries <= 0) {
logger.log(Level.INFO, "Async tasks times out after to many tries {0}", pendingTasks);
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
break;
}
for (Iterator<Integer> iterator = pendingTasks.iterator(); iterator.hasNext(); ) {
int taskId = iterator.next();
if (!scheduler.isCurrentlyRunning(taskId)) {
iterator.remove();
progress++;
logger.log(Level.INFO, "Progress: {0} / {1}", new Object[]{progress, pendingTasks.size()});
}
}
tries--;
}
if (dataSource != null) {
dataSource.close();
}
}
private List<Integer> getPendingTasks() {
List<Integer> pendingTasks = new ArrayList<>();
//returns only the async tasks
for (BukkitWorker pendingTask : scheduler.getActiveWorkers()) {
if (pendingTask.getOwner().equals(plugin)
//it's not a periodic task
&& !scheduler.isQueued(pendingTask.getTaskId())) {
pendingTasks.add(pendingTask.getTaskId());
}
}
return pendingTasks;
}
}

View File

@ -1,97 +0,0 @@
package fr.xephi.authme.listener;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerInfoAction;
import com.comphenix.protocol.wrappers.PlayerInfoData;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.util.BukkitService;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.logging.Level;
public class AuthMeTablistPacketAdapter extends PacketAdapter {
private final BukkitService bukkitService;
@Inject
public AuthMeTablistPacketAdapter(AuthMe plugin, BukkitService bukkitService) {
super(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.PLAYER_INFO);
this.bukkitService = bukkitService;
}
@Override
public void onPacketSending(PacketEvent event) {
if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO) {
//this hides the tablist for the new joining players. Already playing users will see the new player
try {
if (!PlayerCache.getInstance().isAuthenticated(event.getPlayer().getName().toLowerCase())) {
event.setCancelled(true);
}
} catch (FieldAccessException e) {
ConsoleLogger.logException("Couldn't access field", e);
}
}
}
public void sendTablist(Player receiver) {
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(receiver);
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
NativeGameMode gamemode = NativeGameMode.fromBukkit(receiver.getGameMode());
WrappedChatComponent displayName = WrappedChatComponent.fromText(receiver.getDisplayName());
PlayerInfoData playerInfoData = new PlayerInfoData(gameProfile, 0, gamemode, displayName);
//add info containing the skin data
PacketContainer addInfo = protocolManager.createPacket(PacketType.Play.Server.PLAYER_INFO);
addInfo.getPlayerInfoAction().write(0, PlayerInfoAction.ADD_PLAYER);
addInfo.getPlayerInfoDataLists().write(0, Arrays.asList(playerInfoData));
try {
//adds the skin
protocolManager.sendServerPacket(receiver, addInfo);
} catch (InvocationTargetException ex) {
plugin.getLogger().log(Level.SEVERE, "Exception sending instant skin change packet", ex);
}
//triggers an update for others player to see them
for (Player onlinePlayer : bukkitService.getOnlinePlayers()) {
if (onlinePlayer.equals(receiver) || !receiver.canSee(onlinePlayer)) {
continue;
}
//removes the entity and display them
receiver.hidePlayer(onlinePlayer);
receiver.showPlayer(onlinePlayer);
}
}
public void register() {
if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.BOUNTIFUL_UPDATE)) {
ProtocolLibrary.getProtocolManager().addPacketListener(this);
} else {
ConsoleLogger.info("The hideTablist feature is not compatible with your minecraft version");
ConsoleLogger.info("It requires 1.8+. Disabling the hideTablist feature...");
}
}
public void unregister() {
ProtocolLibrary.getProtocolManager().removePacketListener(this);
}
}

View File

@ -7,7 +7,7 @@ import org.bukkit.event.block.BlockPlaceEvent;
import javax.inject.Inject; import javax.inject.Inject;
public class AuthMeBlockListener implements Listener { public class BlockListener implements Listener {
@Inject @Inject
private ListenerService listenerService; private ListenerService listenerService;

View File

@ -1,6 +1,7 @@
package fr.xephi.authme.listener; package fr.xephi.authme.listener;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile; import org.bukkit.entity.Projectile;
@ -20,14 +21,14 @@ import javax.inject.Inject;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
public class AuthMeEntityListener implements Listener { public class EntityListener implements Listener {
private final ListenerService listenerService; private final ListenerService listenerService;
private Method getShooter; private Method getShooter;
private boolean shooterIsLivingEntity; private boolean shooterIsLivingEntity;
@Inject @Inject
AuthMeEntityListener(ListenerService listenerService) { EntityListener(ListenerService listenerService) {
this.listenerService = listenerService; this.listenerService = listenerService;
try { try {
getShooter = Projectile.class.getDeclaredMethod("getShooter"); getShooter = Projectile.class.getDeclaredMethod("getShooter");
@ -39,7 +40,7 @@ public class AuthMeEntityListener implements Listener {
// Note #360: npc status can be used to bypass security!!! // Note #360: npc status can be used to bypass security!!!
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onEntityDamage(EntityDamageEvent event) { public void onDamage(EntityDamageEvent event) {
if (listenerService.shouldCancelEvent(event)) { if (listenerService.shouldCancelEvent(event)) {
event.getEntity().setFireTicks(0); event.getEntity().setFireTicks(0);
event.setDamage(0); event.setDamage(0);
@ -48,16 +49,16 @@ public class AuthMeEntityListener implements Listener {
} }
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onEntityTarget(EntityTargetEvent event) { public void onAttack(EntityDamageByEntityEvent event) {
if (listenerService.shouldCancelEvent(event)) { if (listenerService.shouldCancelEvent(event.getDamager())) {
event.setTarget(null);
event.setCancelled(true); event.setCancelled(true);
} }
} }
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onDamage(EntityDamageByEntityEvent event) { public void onEntityTarget(EntityTargetEvent event) {
if (listenerService.shouldCancelEvent(event)) { if (listenerService.shouldCancelEvent(event)) {
event.setTarget(null);
event.setCancelled(true); event.setCancelled(true);
} }
} }

View File

@ -6,9 +6,9 @@ import fr.xephi.authme.util.StringUtils;
/** /**
* Exception thrown when a verification has failed. * Exception thrown when a verification has failed.
*/ */
@SuppressWarnings("serial")
public class FailedVerificationException extends Exception { public class FailedVerificationException extends Exception {
private static final long serialVersionUID = 3903242223297960699L;
private final MessageKey reason; private final MessageKey reason;
private final String[] args; private final String[] args;

View File

@ -4,17 +4,15 @@ import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.hooks.PluginHooks;
import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.util.ValidationService;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityEvent; import org.bukkit.event.entity.EntityEvent;
import org.bukkit.event.player.PlayerEvent; import org.bukkit.event.player.PlayerEvent;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.HashSet;
import java.util.Set;
/** /**
* Service class for the AuthMe listeners to determine whether an event should be canceled. * Service class for the AuthMe listeners to determine whether an event should be canceled.
@ -24,16 +22,18 @@ class ListenerService implements SettingsDependent {
private final DataSource dataSource; private final DataSource dataSource;
private final PluginHooks pluginHooks; private final PluginHooks pluginHooks;
private final PlayerCache playerCache; private final PlayerCache playerCache;
private final ValidationService validationService;
private boolean isRegistrationForced; private boolean isRegistrationForced;
private Set<String> unrestrictedNames;
@Inject @Inject
ListenerService(NewSetting settings, DataSource dataSource, PluginHooks pluginHooks, PlayerCache playerCache) { ListenerService(Settings settings, DataSource dataSource, PluginHooks pluginHooks,
PlayerCache playerCache, ValidationService validationService) {
this.dataSource = dataSource; this.dataSource = dataSource;
this.pluginHooks = pluginHooks; this.pluginHooks = pluginHooks;
this.playerCache = playerCache; this.playerCache = playerCache;
loadSettings(settings); this.validationService = validationService;
reload(settings);
} }
/** /**
@ -44,10 +44,19 @@ class ListenerService implements SettingsDependent {
*/ */
public boolean shouldCancelEvent(EntityEvent event) { public boolean shouldCancelEvent(EntityEvent event) {
Entity entity = event.getEntity(); Entity entity = event.getEntity();
return shouldCancelEvent(entity);
}
/**
* Returns, based on the entity associated with the event, whether or not the event should be canceled.
*
* @param entity the player entity to verify
* @return true if the associated event should be canceled, false otherwise
*/
public boolean shouldCancelEvent(Entity entity) {
if (entity == null || !(entity instanceof Player)) { if (entity == null || !(entity instanceof Player)) {
return false; return false;
} }
Player player = (Player) entity; Player player = (Player) entity;
return shouldCancelEvent(player); return shouldCancelEvent(player);
} }
@ -74,10 +83,8 @@ class ListenerService implements SettingsDependent {
} }
@Override @Override
public void loadSettings(NewSetting settings) { public void reload(Settings settings) {
isRegistrationForced = settings.getProperty(RegistrationSettings.FORCE); isRegistrationForced = settings.getProperty(RegistrationSettings.FORCE);
// Keep unrestricted names as Set for more efficient contains()
unrestrictedNames = new HashSet<>(settings.getProperty(RestrictionSettings.UNRESTRICTED_NAMES));
} }
/** /**
@ -88,7 +95,7 @@ class ListenerService implements SettingsDependent {
* @return true if the player may play, false otherwise * @return true if the player may play, false otherwise
*/ */
private boolean checkAuth(String name) { private boolean checkAuth(String name) {
if (isUnrestricted(name) || playerCache.isAuthenticated(name)) { if (validationService.isUnrestricted(name) || playerCache.isAuthenticated(name)) {
return true; return true;
} }
if (!isRegistrationForced && !dataSource.isAuthAvailable(name)) { if (!isRegistrationForced && !dataSource.isAuthAvailable(name)) {
@ -96,14 +103,4 @@ class ListenerService implements SettingsDependent {
} }
return false; return false;
} }
/**
* Checks if the name is unrestricted according to the configured settings.
*
* @param name the name to verify
* @return true if unrestricted, false otherwise
*/
private boolean isUnrestricted(String name) {
return unrestrictedNames.contains(name.toLowerCase());
}
} }

View File

@ -9,12 +9,13 @@ import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages; import fr.xephi.authme.output.Messages;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.permission.PlayerStatePermission;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.ProtectionSettings; import fr.xephi.authme.settings.properties.ProtectionSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.StringUtils;
import fr.xephi.authme.util.Utils;
import fr.xephi.authme.util.ValidationService; import fr.xephi.authme.util.ValidationService;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -31,7 +32,7 @@ import java.util.regex.Pattern;
class OnJoinVerifier implements Reloadable { class OnJoinVerifier implements Reloadable {
@Inject @Inject
private NewSetting settings; private Settings settings;
@Inject @Inject
private DataSource dataSource; private DataSource dataSource;
@Inject @Inject
@ -56,13 +57,7 @@ class OnJoinVerifier implements Reloadable {
@Override @Override
public void reload() { public void reload() {
String nickRegEx = settings.getProperty(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS); String nickRegEx = settings.getProperty(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS);
try { nicknamePattern = Utils.safePatternCompile(nickRegEx);
nicknamePattern = Pattern.compile(nickRegEx);
} catch (Exception e) {
nicknamePattern = Pattern.compile(".*?");
ConsoleLogger.showError("Nickname pattern is not a valid regular expression! "
+ "Fallback to allowing all nicknames");
}
} }
/** /**
@ -73,7 +68,7 @@ class OnJoinVerifier implements Reloadable {
*/ */
public void checkAntibot(String playerName, boolean isAuthAvailable) throws FailedVerificationException { public void checkAntibot(String playerName, boolean isAuthAvailable) throws FailedVerificationException {
if (antiBot.getAntiBotStatus() == AntiBot.AntiBotStatus.ACTIVE && !isAuthAvailable) { if (antiBot.getAntiBotStatus() == AntiBot.AntiBotStatus.ACTIVE && !isAuthAvailable) {
antiBot.antibotKicked.addIfAbsent(playerName); antiBot.addPlayerKick(playerName);
throw new FailedVerificationException(MessageKey.KICK_ANTIBOT); throw new FailedVerificationException(MessageKey.KICK_ANTIBOT);
} }
} }
@ -162,15 +157,15 @@ class OnJoinVerifier implements Reloadable {
} }
/** /**
* Checks that the player's country is admitted if he is not registered. * Checks that the player's country is admitted.
* *
* @param isAuthAvailable whether or not the user is registered * @param isAuthAvailable whether or not the user is registered
* @param event the login event of the player * @param playerIp the ip address of the player
*/ */
public void checkPlayerCountry(boolean isAuthAvailable, public void checkPlayerCountry(boolean isAuthAvailable,
PlayerLoginEvent event) throws FailedVerificationException { String playerIp) throws FailedVerificationException {
if (!isAuthAvailable && settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)) { if ((!isAuthAvailable || settings.getProperty(ProtectionSettings.ENABLE_PROTECTION_REGISTERED))
String playerIp = event.getAddress().getHostAddress(); && settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)) {
if (!validationService.isCountryAdmitted(playerIp)) { if (!validationService.isCountryAdmitted(playerIp)) {
throw new FailedVerificationException(MessageKey.COUNTRY_BANNED_ERROR); throw new FailedVerificationException(MessageKey.COUNTRY_BANNED_ERROR);
} }

View File

@ -6,13 +6,14 @@ import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages; import fr.xephi.authme.output.Messages;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.TeleportationService;
import fr.xephi.authme.util.ValidationService;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -23,7 +24,6 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerBedEnterEvent; import org.bukkit.event.player.PlayerBedEnterEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerDropItemEvent;
@ -51,12 +51,12 @@ import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOW_UNAU
/** /**
* Listener class for player events. * Listener class for player events.
*/ */
public class AuthMePlayerListener implements Listener { public class PlayerListener implements Listener {
public static final ConcurrentHashMap<String, String> joinMessage = new ConcurrentHashMap<>(); public static final ConcurrentHashMap<String, String> joinMessage = new ConcurrentHashMap<>();
@Inject @Inject
private NewSetting settings; private Settings settings;
@Inject @Inject
private Messages m; private Messages m;
@Inject @Inject
@ -73,6 +73,10 @@ public class AuthMePlayerListener implements Listener {
private OnJoinVerifier onJoinVerifier; private OnJoinVerifier onJoinVerifier;
@Inject @Inject
private ListenerService listenerService; private ListenerService listenerService;
@Inject
private TeleportationService teleportationService;
@Inject
private ValidationService validationService;
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
@ -141,10 +145,6 @@ public class AuthMePlayerListener implements Listener {
if (!settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)) { if (!settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)) {
// "cancel" the event // "cancel" the event
event.setTo(event.getFrom()); event.setTo(event.getFrom());
if (settings.getProperty(RestrictionSettings.REMOVE_SPEED)) {
player.setFlySpeed(0.0f);
player.setWalkSpeed(0.0f);
}
return; return;
} }
@ -189,47 +189,24 @@ public class AuthMePlayerListener implements Listener {
} }
} }
@EventHandler(priority = EventPriority.LOW) @EventHandler(priority = EventPriority.NORMAL)
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
final Player player = event.getPlayer(); final Player player = event.getPlayer();
if (player != null) { teleportationService.teleportNewPlayerToFirstSpawn(player);
// Schedule login task so works after the prelogin management.performJoin(player);
// (Fix found by Koolaid5000)
bukkitService.runTask(new Runnable() {
@Override
public void run() {
management.performJoin(player);
}
});
}
} }
// Note ljacqu 20160528: AsyncPlayerPreLoginEvent is not fired by all servers in offline mode // Note #831: AsyncPlayerPreLoginEvent is not fired by all servers in offline mode
// e.g. CraftBukkit does not. So we need to run crucial things in onPlayerLogin, too // e.g. CraftBukkit does not fire it. So we need to run crucial things with PlayerLoginEvent.
@EventHandler(priority = EventPriority.HIGHEST) // Single session feature can be implemented for Spigot and CraftBukkit by canceling a kick
public void onPreLogin(AsyncPlayerPreLoginEvent event) { // event caused by "logged in from another location". The nicer way, but only for Spigot, would be
final String name = event.getName().toLowerCase(); // to check in the AsyncPlayerPreLoginEvent. To support all servers, we use the less nice way.
final boolean isAuthAvailable = dataSource.isAuthAvailable(event.getName());
try { @EventHandler(priority = EventPriority.LOW)
// Potential performance improvement: make checkAntiBot not require `isAuthAvailable` info and use
// "checkKickNonRegistered" as last -> no need to query the DB before checking antibot / name
onJoinVerifier.checkAntibot(name, isAuthAvailable);
onJoinVerifier.checkKickNonRegistered(isAuthAvailable);
onJoinVerifier.checkIsValidName(name);
// Note #760: Single session must be checked here - checking with PlayerLoginEvent is too late and
// the first connection will have been kicked. This means this feature doesn't work on CraftBukkit.
onJoinVerifier.checkSingleSession(name);
} catch (FailedVerificationException e) {
event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs()));
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerLogin(PlayerLoginEvent event) { public void onPlayerLogin(PlayerLoginEvent event) {
final Player player = event.getPlayer(); final Player player = event.getPlayer();
if (Utils.isUnrestricted(player)) { final String name = player.getName();
if (validationService.isUnrestricted(name)) {
return; return;
} else if (onJoinVerifier.refusePlayerForFullServer(event)) { } else if (onJoinVerifier.refusePlayerForFullServer(event)) {
return; return;
@ -237,16 +214,20 @@ public class AuthMePlayerListener implements Listener {
return; return;
} }
final String name = player.getName().toLowerCase();
final PlayerAuth auth = dataSource.getAuth(player.getName());
final boolean isAuthAvailable = (auth != null);
try { try {
onJoinVerifier.checkAntibot(name, isAuthAvailable); // Fast stuff
onJoinVerifier.checkKickNonRegistered(isAuthAvailable); onJoinVerifier.checkSingleSession(name);
onJoinVerifier.checkIsValidName(name); onJoinVerifier.checkIsValidName(name);
// Get the auth later as this may cause the single session check to fail
// Slow stuff
final PlayerAuth auth = dataSource.getAuth(name);
final boolean isAuthAvailable = (auth != null);
final String lowerName = name.toLowerCase();
onJoinVerifier.checkAntibot(lowerName, isAuthAvailable);
onJoinVerifier.checkKickNonRegistered(isAuthAvailable);
onJoinVerifier.checkNameCasing(player, auth); onJoinVerifier.checkNameCasing(player, auth);
onJoinVerifier.checkPlayerCountry(isAuthAvailable, event); onJoinVerifier.checkPlayerCountry(isAuthAvailable, event.getAddress().getHostAddress());
} catch (FailedVerificationException e) { } catch (FailedVerificationException e) {
event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs())); event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs()));
event.setResult(PlayerLoginEvent.Result.KICK_OTHER); event.setResult(PlayerLoginEvent.Result.KICK_OTHER);
@ -254,6 +235,7 @@ public class AuthMePlayerListener implements Listener {
} }
antiBot.handlePlayerJoin(player); antiBot.handlePlayerJoin(player);
teleportationService.teleportOnJoin(player);
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST)
@ -262,21 +244,32 @@ public class AuthMePlayerListener implements Listener {
if (settings.getProperty(RegistrationSettings.REMOVE_LEAVE_MESSAGE)) { if (settings.getProperty(RegistrationSettings.REMOVE_LEAVE_MESSAGE)) {
event.setQuitMessage(null); event.setQuitMessage(null);
} else if (settings.getProperty(RegistrationSettings.REMOVE_UNLOGGED_LEAVE_MESSAGE)) {
if(listenerService.shouldCancelEvent(event)) {
event.setQuitMessage(null);
}
} }
if (antiBot.antibotKicked.contains(player.getName())) { if (antiBot.wasPlayerKicked(player.getName())) {
return; return;
} }
management.performQuit(player, false); management.performQuit(player);
} }
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onPlayerKick(PlayerKickEvent event) { public void onPlayerKick(PlayerKickEvent event) {
Player player = event.getPlayer(); // Note #831: Especially for offline CraftBukkit, we need to catch players being kicked because of
// "logged in from another location" and to cancel their kick
if (settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)
&& event.getReason().contains("You logged in from another location")) {
event.setCancelled(true);
return;
}
if (!antiBot.antibotKicked.contains(player.getName())) { final Player player = event.getPlayer();
management.performQuit(player, true); if (!antiBot.wasPlayerKicked(player.getName())) {
management.performQuit(player);
} }
} }

View File

@ -10,7 +10,7 @@ import javax.inject.Inject;
/** /**
* Listener of player events for events introduced in Minecraft 1.6. * Listener of player events for events introduced in Minecraft 1.6.
*/ */
public class AuthMePlayerListener16 implements Listener { public class PlayerListener16 implements Listener {
@Inject @Inject
private ListenerService listenerService; private ListenerService listenerService;

View File

@ -10,7 +10,7 @@ import javax.inject.Inject;
/** /**
* Listener of player events for events introduced in Minecraft 1.8. * Listener of player events for events introduced in Minecraft 1.8.
*/ */
public class AuthMePlayerListener18 implements Listener { public class PlayerListener18 implements Listener {
@Inject @Inject
private ListenerService listenerService; private ListenerService listenerService;

View File

@ -0,0 +1,25 @@
package fr.xephi.authme.listener;
import javax.inject.Inject;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerSwapHandItemsEvent;
/**
* Listener of player events for events introduced in Minecraft 1.9.
*/
public class PlayerListener19 implements Listener {
@Inject
private ListenerService listenerService;
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerSwapHandItems(PlayerSwapHandItemsEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
}

View File

@ -1,53 +1,30 @@
package fr.xephi.authme.listener; package fr.xephi.authme.listener;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.hooks.PluginHooks;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.listener.protocollib.ProtocolLibService;
import fr.xephi.authme.output.Messages;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.ProtectionSettings;
import fr.xephi.authme.util.ValidationService;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginDisableEvent; import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.event.server.PluginEnableEvent; import org.bukkit.event.server.PluginEnableEvent;
import org.bukkit.event.server.ServerListPingEvent;
import javax.inject.Inject; import javax.inject.Inject;
/** /**
*/ */
public class AuthMeServerListener implements Listener { public class ServerListener implements Listener {
@Inject
private AuthMe plugin;
@Inject
private Messages messages;
@Inject
private NewSetting settings;
@Inject @Inject
private PluginHooks pluginHooks; private PluginHooks pluginHooks;
@Inject @Inject
private SpawnLoader spawnLoader; private SpawnLoader spawnLoader;
@Inject @Inject
private ValidationService validationService; private ProtocolLibService protocolLibService;
@Inject @Inject
private PermissionsManager permissionsManager; private PermissionsManager permissionsManager;
@EventHandler(priority = EventPriority.HIGHEST)
public void onServerPing(ServerListPingEvent event) {
if (settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)) {
String playerIp = event.getAddress().getHostAddress();
if (!validationService.isCountryAdmitted(playerIp)) {
event.setMotd(messages.retrieveSingle(MessageKey.COUNTRY_BANNED_ERROR));
}
}
}
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST)
public void onPluginDisable(PluginDisableEvent event) { public void onPluginDisable(PluginDisableEvent event) {
// Make sure the plugin instance isn't null // Make sure the plugin instance isn't null
@ -72,13 +49,9 @@ public class AuthMeServerListener implements Listener {
} else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) { } else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) {
spawnLoader.unloadEssentialsSpawn(); spawnLoader.unloadEssentialsSpawn();
ConsoleLogger.info("EssentialsSpawn has been disabled: unhooking"); ConsoleLogger.info("EssentialsSpawn has been disabled: unhooking");
} } else if ("ProtocolLib".equalsIgnoreCase(pluginName)) {
protocolLibService.disable();
if (pluginName.equalsIgnoreCase("ProtocolLib")) { ConsoleLogger.warning("ProtocolLib has been disabled, unhooking packet adapters!");
plugin.inventoryProtector = null;
plugin.tablistHider = null;
plugin.tabComplete = null;
ConsoleLogger.showError("ProtocolLib has been disabled, unhook packet inventory protection!");
} }
} }
@ -102,10 +75,8 @@ public class AuthMeServerListener implements Listener {
pluginHooks.tryHookToCombatPlus(); pluginHooks.tryHookToCombatPlus();
} else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) { } else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) {
spawnLoader.loadEssentialsSpawn(); spawnLoader.loadEssentialsSpawn();
} } else if ("ProtocolLib".equalsIgnoreCase(pluginName)) {
protocolLibService.setup();
if (pluginName.equalsIgnoreCase("ProtocolLib")) {
plugin.checkProtocolLib();
} }
} }
} }

View File

@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package fr.xephi.authme.listener; package fr.xephi.authme.listener.protocollib;
import com.comphenix.protocol.PacketType; import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.ProtocolLibrary;
@ -22,24 +22,16 @@ import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketEvent;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.settings.Settings;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Collections;
import java.util.logging.Level;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory; import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.logging.Level;
import org.apache.commons.lang.reflect.MethodUtils; class InventoryPacketAdapter extends PacketAdapter {
public class AuthMeInventoryPacketAdapter extends PacketAdapter {
private static final int PLAYER_INVENTORY = 0; private static final int PLAYER_INVENTORY = 0;
// http://wiki.vg/Inventory#Inventory (0-4 crafting, 5-8 armor, 9-35 main inventory, 36-44 hotbar, 45 off hand) // http://wiki.vg/Inventory#Inventory (0-4 crafting, 5-8 armor, 9-35 main inventory, 36-44 hotbar, 45 off hand)
@ -48,12 +40,8 @@ public class AuthMeInventoryPacketAdapter extends PacketAdapter {
private static final int ARMOR_SIZE = 4; private static final int ARMOR_SIZE = 4;
private static final int MAIN_SIZE = 27; private static final int MAIN_SIZE = 27;
private static final int HOTBAR_SIZE = 9; private static final int HOTBAR_SIZE = 9;
private static final int OFF_HAND_POSITION = 45;
private final boolean offHandSupported = MethodUtils public InventoryPacketAdapter(AuthMe plugin) {
.getAccessibleMethod(PlayerInventory.class, "getItemInOffHand", new Class[]{}) != null;
public AuthMeInventoryPacketAdapter(AuthMe plugin) {
super(plugin, PacketType.Play.Server.SET_SLOT, PacketType.Play.Server.WINDOW_ITEMS); super(plugin, PacketType.Play.Server.SET_SLOT, PacketType.Play.Server.WINDOW_ITEMS);
} }
@ -63,8 +51,7 @@ public class AuthMeInventoryPacketAdapter extends PacketAdapter {
PacketContainer packet = packetEvent.getPacket(); PacketContainer packet = packetEvent.getPacket();
byte windowId = packet.getIntegers().read(0).byteValue(); byte windowId = packet.getIntegers().read(0).byteValue();
if (windowId == PLAYER_INVENTORY && Settings.protectInventoryBeforeLogInEnabled if (windowId == PLAYER_INVENTORY && !PlayerCache.getInstance().isAuthenticated(player.getName())) {
&& !PlayerCache.getInstance().isAuthenticated(player.getName())) {
packetEvent.setCancelled(true); packetEvent.setCancelled(true);
} }
} }
@ -77,52 +64,6 @@ public class AuthMeInventoryPacketAdapter extends PacketAdapter {
ProtocolLibrary.getProtocolManager().removePacketListener(this); ProtocolLibrary.getProtocolManager().removePacketListener(this);
} }
public void sendInventoryPacket(Player player) {
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
PacketContainer inventoryPacket = protocolManager.createPacket(PacketType.Play.Server.WINDOW_ITEMS);
// we are sending our own inventory
inventoryPacket.getIntegers().write(0, PLAYER_INVENTORY);
ItemStack[] playerCrafting = new ItemStack[CRAFTING_SIZE];
Arrays.fill(playerCrafting, new ItemStack(Material.AIR));
ItemStack[] armorContents = player.getInventory().getArmorContents();
ItemStack[] mainInventory = player.getInventory().getContents();
// bukkit saves the armor in reversed order
Collections.reverse(Arrays.asList(armorContents));
// same main inventory. The hotbar is at the beginning but it should be at the end of the array
ItemStack[] hotbar = Arrays.copyOfRange(mainInventory, 0, HOTBAR_SIZE);
ItemStack[] storedInventory = Arrays.copyOfRange(mainInventory, HOTBAR_SIZE, mainInventory.length);
// concat all parts of the inventory together
int inventorySize = CRAFTING_SIZE + ARMOR_SIZE + MAIN_SIZE + HOTBAR_SIZE;
if (offHandSupported) {
inventorySize++;
}
ItemStack[] completeInventory = new ItemStack[inventorySize];
System.arraycopy(playerCrafting, 0, completeInventory, 0, CRAFTING_SIZE);
System.arraycopy(armorContents, 0, completeInventory, CRAFTING_SIZE, ARMOR_SIZE);
// storedInventory and hotbar
System.arraycopy(storedInventory, 0, completeInventory, CRAFTING_SIZE + ARMOR_SIZE, MAIN_SIZE);
System.arraycopy(hotbar, 0, completeInventory, CRAFTING_SIZE + ARMOR_SIZE + MAIN_SIZE, HOTBAR_SIZE);
if (offHandSupported) {
completeInventory[OFF_HAND_POSITION] = player.getInventory().getItemInOffHand();
}
inventoryPacket.getItemArrayModifier().write(0, completeInventory);
try {
protocolManager.sendServerPacket(player, inventoryPacket, false);
} catch (InvocationTargetException invocationExc) {
plugin.getLogger().log(Level.WARNING, "Error during inventory recovery", invocationExc);
}
}
public void sendBlankInventoryPacket(Player player) { public void sendBlankInventoryPacket(Player player) {
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
PacketContainer inventoryPacket = protocolManager.createPacket(PacketType.Play.Server.WINDOW_ITEMS); PacketContainer inventoryPacket = protocolManager.createPacket(PacketType.Play.Server.WINDOW_ITEMS);

View File

@ -0,0 +1,120 @@
package fr.xephi.authme.listener.protocollib;
import ch.jalu.injector.annotations.NoFieldScan;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.util.BukkitService;
import org.bukkit.entity.Player;
import javax.inject.Inject;
@NoFieldScan
public class ProtocolLibService implements SettingsDependent {
/* Packet Adapters */
private InventoryPacketAdapter inventoryPacketAdapter;
private TabCompletePacketAdapter tabCompletePacketAdapter;
/* Settings */
private boolean protectInvBeforeLogin;
private boolean denyTabCompleteBeforeLogin;
/* Service */
private boolean isEnabled;
private AuthMe plugin;
private BukkitService bukkitService;
private PlayerCache playerCache;
@Inject
ProtocolLibService(AuthMe plugin, Settings settings, BukkitService bukkitService, PlayerCache playerCache) {
this.plugin = plugin;
this.bukkitService = bukkitService;
this.playerCache = playerCache;
reload(settings);
}
/**
* Set up the ProtocolLib packet adapters.
*/
public void setup() {
// Check if ProtocolLib is enabled on the server.
if (!plugin.getServer().getPluginManager().isPluginEnabled("ProtocolLib")) {
if (protectInvBeforeLogin) {
ConsoleLogger.warning("WARNING! The protectInventory feature requires ProtocolLib! Disabling it...");
}
if (denyTabCompleteBeforeLogin) {
ConsoleLogger.warning("WARNING! The denyTabComplete feature requires ProtocolLib! Disabling it...");
}
this.isEnabled = false;
return;
}
// Set up packet adapters
if (protectInvBeforeLogin && inventoryPacketAdapter == null) {
inventoryPacketAdapter = new InventoryPacketAdapter(plugin);
inventoryPacketAdapter.register();
} else if (inventoryPacketAdapter != null) {
inventoryPacketAdapter.unregister();
inventoryPacketAdapter = null;
}
if (denyTabCompleteBeforeLogin && tabCompletePacketAdapter == null) {
tabCompletePacketAdapter = new TabCompletePacketAdapter(plugin);
tabCompletePacketAdapter.register();
} else if (tabCompletePacketAdapter != null) {
tabCompletePacketAdapter.unregister();
tabCompletePacketAdapter = null;
}
this.isEnabled = true;
}
public void disable() {
isEnabled = false;
if (inventoryPacketAdapter != null) {
inventoryPacketAdapter.unregister();
inventoryPacketAdapter = null;
}
if (tabCompletePacketAdapter != null) {
tabCompletePacketAdapter.unregister();
tabCompletePacketAdapter = null;
}
}
/**
* Send a packet to the player to give them a blank inventory.
*
* @param player The player to send the packet to.
*/
public void sendBlankInventoryPacket(Player player) {
if (isEnabled && inventoryPacketAdapter != null) {
inventoryPacketAdapter.sendBlankInventoryPacket(player);
}
}
@Override
public void reload(Settings settings) {
boolean oldProtectInventory = this.protectInvBeforeLogin;
this.protectInvBeforeLogin = settings.getProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN);
this.denyTabCompleteBeforeLogin = settings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN);
//it was true and will be deactivated now, so we need to restore the inventory for every player
if (oldProtectInventory && !protectInvBeforeLogin) {
inventoryPacketAdapter.unregister();
for (Player onlinePlayer : bukkitService.getOnlinePlayers()) {
if (!playerCache.isAuthenticated(onlinePlayer.getName())) {
onlinePlayer.updateInventory();
}
}
}
setup();
}
}

View File

@ -1,4 +1,4 @@
package fr.xephi.authme.listener; package fr.xephi.authme.listener.protocollib;
import com.comphenix.protocol.PacketType; import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.ProtocolLibrary;
@ -6,17 +6,13 @@ import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
import javax.inject.Inject; class TabCompletePacketAdapter extends PacketAdapter {
public class AuthMeTabCompletePacketAdapter extends PacketAdapter { public TabCompletePacketAdapter(AuthMe plugin) {
@Inject
public AuthMeTabCompletePacketAdapter(AuthMe plugin) {
super(plugin, ListenerPriority.NORMAL, PacketType.Play.Client.TAB_COMPLETE); super(plugin, ListenerPriority.NORMAL, PacketType.Play.Client.TAB_COMPLETE);
} }
@ -28,7 +24,7 @@ public class AuthMeTabCompletePacketAdapter extends PacketAdapter {
event.setCancelled(true); event.setCancelled(true);
} }
} catch (FieldAccessException e) { } catch (FieldAccessException e) {
ConsoleLogger.showError("Couldn't access field."); ConsoleLogger.warning("Couldn't access field.");
} }
} }
} }

View File

@ -3,47 +3,74 @@ package fr.xephi.authme.mail;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.StringUtils;
import org.apache.commons.mail.EmailConstants; import org.apache.commons.mail.EmailConstants;
import org.apache.commons.mail.EmailException; import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail; import org.apache.commons.mail.HtmlEmail;
import org.bukkit.Bukkit;
import javax.activation.DataSource; import javax.activation.DataSource;
import javax.activation.FileDataSource; import javax.activation.FileDataSource;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.inject.Inject;
import javax.mail.Session; import javax.mail.Session;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.security.Security; import java.security.Security;
import java.util.Properties; import java.util.Properties;
import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_ACCOUNT;
import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_PASSWORD;
/** /**
* @author Xephi59 * @author Xephi59
*/ */
public class SendMailSSL { public class SendMailSSL {
private final AuthMe plugin; @Inject
private final NewSetting settings; private AuthMe plugin;
@Inject
private Settings settings;
@Inject
private BukkitService bukkitService;
public SendMailSSL(AuthMe plugin, NewSetting settings) { SendMailSSL() {
this.plugin = plugin;
this.settings = settings;
} }
public void main(final PlayerAuth auth, final String newPass) { /**
final String mailText = replaceMailTags(settings.getEmailMessage(), plugin, auth, newPass); * Returns whether all necessary settings are set for sending mails.
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() { *
* @return true if the necessary email settings are set, false otherwise
*/
public boolean hasAllInformation() {
return !settings.getProperty(MAIL_ACCOUNT).isEmpty()
&& !settings.getProperty(MAIL_PASSWORD).isEmpty();
}
/**
* Sends an email to the user with his new password.
*
* @param auth the player auth of the player
* @param newPass the new password
*/
public void sendPasswordMail(final PlayerAuth auth, final String newPass) {
if (!hasAllInformation()) {
ConsoleLogger.warning("Cannot perform email registration: not all email settings are complete");
return;
}
final String mailText = replaceMailTags(settings.getEmailMessage(), auth, newPass);
bukkitService.runTaskAsynchronously(new Runnable() {
@Override @Override
public void run() { public void run() {
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
HtmlEmail email; HtmlEmail email;
try { try {
email = initializeMail(auth, settings); email = initializeMail(auth.getEmail());
} catch (EmailException e) { } catch (EmailException e) {
ConsoleLogger.logException("Failed to create email with the given settings:", e); ConsoleLogger.logException("Failed to create email with the given settings:", e);
return; return;
@ -54,7 +81,7 @@ public class SendMailSSL {
File file = null; File file = null;
if (settings.getProperty(EmailSettings.PASSWORD_AS_IMAGE)) { if (settings.getProperty(EmailSettings.PASSWORD_AS_IMAGE)) {
try { try {
file = generateImage(auth, plugin, newPass); file = generateImage(auth.getNickname(), plugin, newPass);
content = embedImageIntoEmailContent(file, email, content); content = embedImageIntoEmailContent(file, email, content);
} catch (IOException | EmailException e) { } catch (IOException | EmailException e) {
ConsoleLogger.logException( ConsoleLogger.logException(
@ -67,13 +94,12 @@ public class SendMailSSL {
file.delete(); file.delete();
} }
} }
}); });
} }
private static File generateImage(PlayerAuth auth, AuthMe plugin, String newPass) throws IOException { private static File generateImage(String name, AuthMe plugin, String newPass) throws IOException {
ImageGenerator gen = new ImageGenerator(newPass); ImageGenerator gen = new ImageGenerator(newPass);
File file = new File(plugin.getDataFolder(), auth.getNickname() + "_new_pass.jpg"); File file = new File(plugin.getDataFolder(), name + "_new_pass.jpg");
ImageIO.write(gen.generateImage(), "jpg", file); ImageIO.write(gen.generateImage(), "jpg", file);
return file; return file;
} }
@ -85,8 +111,7 @@ public class SendMailSSL {
return content.replace("<image />", "<img src=\"cid:" + tag + "\">"); return content.replace("<image />", "<img src=\"cid:" + tag + "\">");
} }
private static HtmlEmail initializeMail(PlayerAuth auth, NewSetting settings) private HtmlEmail initializeMail(String emailAddress) throws EmailException {
throws EmailException {
String senderMail = settings.getProperty(EmailSettings.MAIL_ACCOUNT); String senderMail = settings.getProperty(EmailSettings.MAIL_ACCOUNT);
String senderName = StringUtils.isEmpty(settings.getProperty(EmailSettings.MAIL_SENDER_NAME)) String senderName = StringUtils.isEmpty(settings.getProperty(EmailSettings.MAIL_SENDER_NAME))
? senderMail ? senderMail
@ -98,12 +123,12 @@ public class SendMailSSL {
email.setCharset(EmailConstants.UTF_8); email.setCharset(EmailConstants.UTF_8);
email.setSmtpPort(port); email.setSmtpPort(port);
email.setHostName(settings.getProperty(EmailSettings.SMTP_HOST)); email.setHostName(settings.getProperty(EmailSettings.SMTP_HOST));
email.addTo(auth.getEmail()); email.addTo(emailAddress);
email.setFrom(senderMail, senderName); email.setFrom(senderMail, senderName);
email.setSubject(settings.getProperty(EmailSettings.RECOVERY_MAIL_SUBJECT)); email.setSubject(settings.getProperty(EmailSettings.RECOVERY_MAIL_SUBJECT));
email.setAuthentication(senderMail, mailPassword); email.setAuthentication(senderMail, mailPassword);
setPropertiesForPort(email, port, settings); setPropertiesForPort(email, port);
return email; return email;
} }
@ -124,15 +149,14 @@ public class SendMailSSL {
} }
} }
private static String replaceMailTags(String mailText, AuthMe plugin, PlayerAuth auth, String newPass) { private String replaceMailTags(String mailText, PlayerAuth auth, String newPass) {
return mailText return mailText
.replace("<playername />", auth.getNickname()) .replace("<playername />", auth.getNickname())
.replace("<servername />", plugin.getServer().getServerName()) .replace("<servername />", plugin.getServer().getServerName())
.replace("<generatedpass />", newPass); .replace("<generatedpass />", newPass);
} }
private static void setPropertiesForPort(HtmlEmail email, int port, NewSetting settings) private void setPropertiesForPort(HtmlEmail email, int port) throws EmailException {
throws EmailException {
switch (port) { switch (port) {
case 587: case 587:
String oAuth2Token = settings.getProperty(EmailSettings.OAUTH2_TOKEN); String oAuth2Token = settings.getProperty(EmailSettings.OAUTH2_TOKEN);

View File

@ -2,9 +2,9 @@ package fr.xephi.authme.output;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.Message;
/** /**
@ -12,7 +12,8 @@ import org.apache.logging.log4j.message.Message;
* *
* @author Xephi59 * @author Xephi59
*/ */
public class Log4JFilter implements Filter { @SuppressWarnings("serial")
public class Log4JFilter extends AbstractFilter {
/** /**
* Constructor. * Constructor.
@ -50,42 +51,30 @@ public class Log4JFilter implements Filter {
} }
@Override @Override
public Result filter(LogEvent record) { public Result filter(LogEvent event) {
if (record == null) { Message candidate = null;
return Result.NEUTRAL; if(event != null) {
candidate = event.getMessage();
} }
return validateMessage(record.getMessage()); return validateMessage(candidate);
} }
@Override @Override
public Result filter(Logger arg0, Level arg1, Marker arg2, String message, Object... arg4) { public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
if (message == null) { return validateMessage(msg);
return Result.NEUTRAL; }
@Override
public Result filter(Logger logger, Level level, Marker marker, String msg, Object... params) {
return validateMessage(msg);
}
@Override
public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
String candidate = null;
if(msg != null) {
candidate = msg.toString();
} }
return validateMessage(message); return validateMessage(candidate);
} }
@Override
public Result filter(Logger arg0, Level arg1, Marker arg2, Object message, Throwable arg4) {
if (message == null) {
return Result.NEUTRAL;
}
return validateMessage(message.toString());
}
@Override
public Result filter(Logger arg0, Level arg1, Marker arg2, Message message, Throwable arg4) {
return validateMessage(message);
}
@Override
public Result getOnMatch() {
return Result.NEUTRAL;
}
@Override
public Result getOnMismatch() {
return Result.NEUTRAL;
}
} }

View File

@ -0,0 +1,38 @@
package fr.xephi.authme.output;
/**
* Log level.
*/
public enum LogLevel {
/** Info: general messages. */
INFO(3),
/** Fine: more detailed messages that may still be interesting to plugin users. */
FINE(2),
/** Debug: very detailed messages for debugging. */
DEBUG(1);
private int value;
/**
* Constructor.
*
* @param value the log level; the higher the number the more "important" the level.
* A log level enables its number and all above.
*/
LogLevel(int value) {
this.value = value;
}
/**
* Return whether the current log level includes the given log level.
*
* @param level the level to process
* @return true if the level is enabled, false otherwise
*/
public boolean includes(LogLevel level) {
return value <= level.value;
}
}

View File

@ -143,7 +143,11 @@ public enum MessageKey {
ACCOUNTS_OWNED_SELF("accounts_owned_self", "%count"), ACCOUNTS_OWNED_SELF("accounts_owned_self", "%count"),
ACCOUNTS_OWNED_OTHER("accounts_owned_other", "%name", "%count"); ACCOUNTS_OWNED_OTHER("accounts_owned_other", "%name", "%count"),
KICK_FOR_ADMIN_REGISTER("kicked_admin_registered"),
INCOMPLETE_EMAIL_SETTINGS("incomplete_email_settings");
private String key; private String key;
private String[] tags; private String[] tags;

View File

@ -2,13 +2,13 @@ package fr.xephi.authme.output;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import javax.inject.Inject;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@ -18,6 +18,9 @@ import java.io.InputStreamReader;
*/ */
public class Messages implements SettingsDependent { public class Messages implements SettingsDependent {
// Custom Authme tag replaced to new line
private static final String NEWLINE_TAG = "%nl%";
private FileConfiguration configuration; private FileConfiguration configuration;
private String fileName; private String fileName;
private final String defaultFile; private final String defaultFile;
@ -26,12 +29,12 @@ public class Messages implements SettingsDependent {
/** /**
* Constructor. * Constructor.
* *
* @param messageFile The messages file to use * @param settings The settings
* @param defaultFile The file with messages to use as default if missing
*/ */
public Messages(File messageFile, String defaultFile) { @Inject
initializeFile(messageFile); Messages(Settings settings) {
this.defaultFile = defaultFile; reload(settings);
this.defaultFile = settings.getDefaultMessagesFile();
} }
/** /**
@ -70,18 +73,12 @@ public class Messages implements SettingsDependent {
* @return The message split by new lines * @return The message split by new lines
*/ */
public String[] retrieve(MessageKey key) { public String[] retrieve(MessageKey key) {
final String code = key.getKey(); String message = retrieveMessage(key);
String message = configuration.getString(code); if (message.isEmpty()) {
// Return empty array instead of array with 1 empty string as entry
if (message == null) {
ConsoleLogger.showError("Error getting message with key '" + code + "'. "
+ "Please verify your config file at '" + fileName + "'");
return formatMessage(getDefault(code));
}
if(message.isEmpty()) {
return new String[0]; return new String[0];
} }
return formatMessage(message); return message.split("\n");
} }
/** /**
@ -90,8 +87,16 @@ public class Messages implements SettingsDependent {
* @param key The message key to retrieve * @param key The message key to retrieve
* @return The message from the file * @return The message from the file
*/ */
public String retrieveSingle(MessageKey key) { private String retrieveMessage(MessageKey key) {
return StringUtils.join("\n", retrieve(key)); final String code = key.getKey();
String message = configuration.getString(code);
if (message == null) {
ConsoleLogger.warning("Error getting message with key '" + code + "'. "
+ "Please verify your config file at '" + fileName + "'");
return formatMessage(getDefault(code));
}
return formatMessage(message);
} }
/** /**
@ -104,24 +109,21 @@ public class Messages implements SettingsDependent {
* @return The message from the file with replacements * @return The message from the file with replacements
*/ */
public String retrieveSingle(MessageKey key, String... replacements) { public String retrieveSingle(MessageKey key, String... replacements) {
String message = retrieveSingle(key); String message = retrieveMessage(key);
String[] tags = key.getTags(); String[] tags = key.getTags();
if (replacements.length == tags.length) { if (replacements.length == tags.length) {
for (int i = 0; i < tags.length; ++i) { for (int i = 0; i < tags.length; ++i) {
message = message.replace(tags[i], replacements[i]); message = message.replace(tags[i], replacements[i]);
} }
} else { } else {
ConsoleLogger.showError("Invalid number of replacements for message key '" + key + "'"); ConsoleLogger.warning("Invalid number of replacements for message key '" + key + "'");
} }
return message; return message;
} }
@Override @Override
public void loadSettings(NewSetting settings) { public void reload(Settings settings) {
initializeFile(settings.getMessagesFile()); File messageFile = settings.getMessagesFile();
}
private void initializeFile(File messageFile) {
this.configuration = YamlConfiguration.loadConfiguration(messageFile); this.configuration = YamlConfiguration.loadConfiguration(messageFile);
this.fileName = messageFile.getName(); this.fileName = messageFile.getName();
} }
@ -143,12 +145,9 @@ public class Messages implements SettingsDependent {
return "Error retrieving message '" + code + "'"; return "Error retrieving message '" + code + "'";
} }
private static String[] formatMessage(String message) { private static String formatMessage(String message) {
String[] lines = message.split("&n"); return ChatColor.translateAlternateColorCodes('&', message)
for (int i = 0; i < lines.length; ++i) { .replace(NEWLINE_TAG, "\n");
lines[i] = ChatColor.translateAlternateColorCodes('&', lines[i]);
}
return lines;
} }
} }

View File

@ -2,10 +2,12 @@ package fr.xephi.authme.permission;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.cache.limbo.LimboCache;
import fr.xephi.authme.cache.limbo.LimboPlayer; import fr.xephi.authme.cache.limbo.PlayerData;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import javax.inject.Inject; import javax.inject.Inject;
@ -14,17 +16,21 @@ import java.util.Arrays;
/** /**
* Changes the permission group according to the auth status of the player and the configuration. * Changes the permission group according to the auth status of the player and the configuration.
*/ */
public class AuthGroupHandler { public class AuthGroupHandler implements Reloadable {
@Inject @Inject
private PermissionsManager permissionsManager; private PermissionsManager permissionsManager;
@Inject @Inject
private NewSetting settings; private Settings settings;
@Inject @Inject
private LimboCache limboCache; private LimboCache limboCache;
private String unloggedInGroup;
private String unregisteredGroup;
private String registeredGroup;
AuthGroupHandler() { } AuthGroupHandler() { }
/** /**
@ -44,41 +50,74 @@ public class AuthGroupHandler {
// Make sure group support is available // Make sure group support is available
if (!permissionsManager.hasGroupSupport()) { if (!permissionsManager.hasGroupSupport()) {
ConsoleLogger.showError("The current permissions system doesn't have group support, unable to set group!"); ConsoleLogger.warning("The current permissions system doesn't have group support, unable to set group!");
return false; return false;
} }
switch (group) { switch (group) {
case UNREGISTERED: case UNREGISTERED:
// Remove the other group type groups, set the current group // Remove the other group type groups, set the current group
permissionsManager.removeGroups(player, Arrays.asList(Settings.getRegisteredGroup, Settings.getUnloggedinGroup)); permissionsManager.removeGroups(player, Arrays.asList(registeredGroup, unloggedInGroup));
return permissionsManager.addGroup(player, Settings.unRegisteredGroup); return permissionsManager.addGroup(player, unregisteredGroup);
case REGISTERED: case REGISTERED:
// Remove the other group type groups, set the current group // Remove the other group type groups, set the current group
permissionsManager.removeGroups(player, Arrays.asList(Settings.unRegisteredGroup, Settings.getUnloggedinGroup)); permissionsManager.removeGroups(player, Arrays.asList(unregisteredGroup, unloggedInGroup));
return permissionsManager.addGroup(player, Settings.getRegisteredGroup); return permissionsManager.addGroup(player, registeredGroup);
case NOT_LOGGED_IN: case NOT_LOGGED_IN:
// Remove the other group type groups, set the current group // Remove the other group type groups, set the current group
permissionsManager.removeGroups(player, Arrays.asList(Settings.unRegisteredGroup, Settings.getRegisteredGroup)); permissionsManager.removeGroups(player, Arrays.asList(unregisteredGroup, registeredGroup));
return permissionsManager.addGroup(player, Settings.getUnloggedinGroup); return permissionsManager.addGroup(player, unloggedInGroup);
case LOGGED_IN: case LOGGED_IN:
// Get the limbo player data // Get the player data
LimboPlayer limbo = limboCache.getLimboPlayer(player.getName().toLowerCase()); PlayerData data = limboCache.getPlayerData(player.getName().toLowerCase());
if (limbo == null) if (data == null) {
return false; return false;
}
// Get the players group // Get the players group
String realGroup = limbo.getGroup(); String realGroup = data.getGroup();
// Remove the other group types groups, set the real group // Remove the other group types groups, set the real group
permissionsManager.removeGroups(player, Arrays.asList(Settings.unRegisteredGroup, Settings.getRegisteredGroup, Settings.getUnloggedinGroup)); permissionsManager.removeGroups(player,
Arrays.asList(unregisteredGroup, registeredGroup, unloggedInGroup)
);
return permissionsManager.addGroup(player, realGroup); return permissionsManager.addGroup(player, realGroup);
default: default:
return false; return false;
} }
} }
/**
* TODO: This method requires better explanation.
* <p>
* Set the normal group of a player.
*
* @param player The player.
* @param group The normal group.
*
* @return True on success, false on failure.
*/
public boolean addNormal(Player player, String group) {
// Check whether the permissions check is enabled
if (!settings.getProperty(PluginSettings.ENABLE_PERMISSION_CHECK)) {
return false;
}
// Remove old groups
permissionsManager.removeGroups(player, Arrays.asList(unregisteredGroup, registeredGroup, unloggedInGroup));
// Add the normal group, return the result
return permissionsManager.addGroup(player, group);
}
@Override
public void reload() {
unloggedInGroup = settings.getProperty(SecuritySettings.UNLOGGEDIN_GROUP);
unregisteredGroup = settings.getProperty(HooksSettings.UNREGISTERED_GROUP);
registeredGroup = settings.getProperty(HooksSettings.REGISTERED_GROUP);
}
} }

View File

@ -1,6 +1,6 @@
package fr.xephi.authme.permission; package fr.xephi.authme.permission;
import org.bukkit.command.CommandSender; import org.bukkit.permissions.ServerOperator;
/** /**
* The default permission to fall back to if there is no support for permission nodes. * The default permission to fall back to if there is no support for permission nodes.
@ -10,7 +10,7 @@ public enum DefaultPermission {
/** No one has permission. */ /** No one has permission. */
NOT_ALLOWED("No permission") { NOT_ALLOWED("No permission") {
@Override @Override
public boolean evaluate(CommandSender sender) { public boolean evaluate(ServerOperator sender) {
return false; return false;
} }
}, },
@ -18,15 +18,15 @@ public enum DefaultPermission {
/** Only players with OP status have permission. */ /** Only players with OP status have permission. */
OP_ONLY("OP's only") { OP_ONLY("OP's only") {
@Override @Override
public boolean evaluate(CommandSender sender) { public boolean evaluate(ServerOperator sender) {
return sender.isOp(); return sender != null && sender.isOp();
} }
}, },
/** Everyone is granted permission. */ /** Everyone is granted permission. */
ALLOWED("Everyone allowed") { ALLOWED("Everyone allowed") {
@Override @Override
public boolean evaluate(CommandSender sender) { public boolean evaluate(ServerOperator sender) {
return true; return true;
} }
}; };
@ -48,7 +48,7 @@ public enum DefaultPermission {
* @param sender the sender to process * @param sender the sender to process
* @return true if the sender has permission, false otherwise * @return true if the sender has permission, false otherwise
*/ */
public abstract boolean evaluate(CommandSender sender); public abstract boolean evaluate(ServerOperator sender);
/** /**
* Return the textual representation. * Return the textual representation.

View File

@ -1,25 +1,23 @@
package fr.xephi.authme.permission; package fr.xephi.authme.permission;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.permission.handlers.BPermissionsHandler; import fr.xephi.authme.permission.handlers.BPermissionsHandler;
import fr.xephi.authme.permission.handlers.GroupManagerHandler; import fr.xephi.authme.permission.handlers.GroupManagerHandler;
import fr.xephi.authme.permission.handlers.PermissionHandler; import fr.xephi.authme.permission.handlers.PermissionHandler;
import fr.xephi.authme.permission.handlers.PermissionHandlerException;
import fr.xephi.authme.permission.handlers.PermissionsBukkitHandler; import fr.xephi.authme.permission.handlers.PermissionsBukkitHandler;
import fr.xephi.authme.permission.handlers.PermissionsExHandler; import fr.xephi.authme.permission.handlers.PermissionsExHandler;
import fr.xephi.authme.permission.handlers.VaultHandler; import fr.xephi.authme.permission.handlers.VaultHandler;
import fr.xephi.authme.permission.handlers.ZPermissionsHandler; import fr.xephi.authme.permission.handlers.ZPermissionsHandler;
import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.StringUtils;
import net.milkbowl.vault.permission.Permission;
import org.anjocaido.groupmanager.GroupManager; import org.anjocaido.groupmanager.GroupManager;
import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.tyrannyofheaven.bukkit.zPermissions.ZPermissionsService;
import ru.tehkode.permissions.bukkit.PermissionsEx;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.inject.Inject; import javax.inject.Inject;
@ -38,7 +36,7 @@ import java.util.List;
* @author Tim Visée, http://timvisee.com * @author Tim Visée, http://timvisee.com
* @version 0.3 * @version 0.3
*/ */
public class PermissionsManager { public class PermissionsManager implements Reloadable {
private final Server server; private final Server server;
private final PluginManager pluginManager; private final PluginManager pluginManager;
@ -74,90 +72,17 @@ public class PermissionsManager {
* Setup and hook into the permissions systems. * Setup and hook into the permissions systems.
*/ */
@PostConstruct @PostConstruct
public void setup() { private void setup() {
// Force-unhook from current hooked permissions systems
unhook();
// Loop through all the available permissions system types // Loop through all the available permissions system types
for (PermissionsSystemType type : PermissionsSystemType.values()) { for (PermissionsSystemType type : PermissionsSystemType.values()) {
// Try to find and hook the current plugin if available, print an error if failed
try { try {
// Try to find the plugin for the current permissions system PermissionHandler handler = getPermissionHandler(type);
Plugin plugin = pluginManager.getPlugin(type.getPluginName()); if (handler != null) {
// Show a success message and return
// Make sure a plugin with this name was found this.handler = handler;
if (plugin == null) ConsoleLogger.info("Hooked into " + type.getName() + "!");
continue; return;
// Make sure the plugin is enabled before hooking
if (!plugin.isEnabled()) {
ConsoleLogger.info("Not hooking into " + type.getName() + " because it's disabled!");
continue;
} }
// Use the proper method to hook this plugin
switch (type) {
case PERMISSIONS_EX:
// Get the permissions manager for PermissionsEx and make sure it isn't null
if (PermissionsEx.getPermissionManager() == null) {
ConsoleLogger.info("Failed to hook into " + type.getName() + "!");
continue;
}
handler = new PermissionsExHandler(PermissionsEx.getPermissionManager());
break;
case ESSENTIALS_GROUP_MANAGER:
// Set the plugin instance
handler = new GroupManagerHandler((GroupManager) plugin);
break;
case Z_PERMISSIONS:
// Set the zPermissions service and make sure it's valid
ZPermissionsService zPermissionsService = Bukkit.getServicesManager().load(ZPermissionsService.class);
if (zPermissionsService == null) {
ConsoleLogger.info("Failed to hook into " + type.getName() + "!");
continue;
}
handler = new ZPermissionsHandler(zPermissionsService);
break;
case VAULT:
// Get the permissions provider service
RegisteredServiceProvider<Permission> permissionProvider = this.server.getServicesManager().getRegistration(Permission.class);
if (permissionProvider == null) {
ConsoleLogger.info("Failed to hook into " + type.getName() + "!");
continue;
}
// Get the Vault provider and make sure it's valid
Permission vaultPerms = permissionProvider.getProvider();
if (vaultPerms == null) {
ConsoleLogger.info("Not using " + type.getName() + " because it's disabled!");
continue;
}
handler = new VaultHandler(vaultPerms);
break;
case B_PERMISSIONS:
handler = new BPermissionsHandler();
break;
case PERMISSIONS_BUKKIT:
handler = new PermissionsBukkitHandler();
break;
default:
}
// Show a success message
ConsoleLogger.info("Hooked into " + type.getName() + "!");
// Return the used permissions system type
return;
} catch (Exception ex) { } catch (Exception ex) {
// An error occurred, show a warning message // An error occurred, show a warning message
ConsoleLogger.logException("Error while hooking into " + type.getName(), ex); ConsoleLogger.logException("Error while hooking into " + type.getName(), ex);
@ -168,10 +93,42 @@ public class PermissionsManager {
ConsoleLogger.info("No supported permissions system found! Permissions are disabled!"); ConsoleLogger.info("No supported permissions system found! Permissions are disabled!");
} }
private PermissionHandler getPermissionHandler(PermissionsSystemType type) throws PermissionHandlerException {
// Try to find the plugin for the current permissions system
Plugin plugin = pluginManager.getPlugin(type.getPluginName());
if (plugin == null) {
return null;
}
// Make sure the plugin is enabled before hooking
if (!plugin.isEnabled()) {
ConsoleLogger.info("Not hooking into " + type.getName() + " because it's disabled!");
return null;
}
switch (type) {
case PERMISSIONS_EX:
return new PermissionsExHandler();
case ESSENTIALS_GROUP_MANAGER:
return new GroupManagerHandler((GroupManager) plugin);
case Z_PERMISSIONS:
return new ZPermissionsHandler();
case VAULT:
return new VaultHandler(server);
case B_PERMISSIONS:
return new BPermissionsHandler();
case PERMISSIONS_BUKKIT:
return new PermissionsBukkitHandler();
default:
throw new IllegalStateException("Unhandled permission type '" + type + "'");
}
}
/** /**
* Break the hook with all permission systems. * Break the hook with all permission systems.
*/ */
public void unhook() { private void unhook() {
// Reset the current used permissions system // Reset the current used permissions system
this.handler = null; this.handler = null;
@ -182,11 +139,12 @@ public class PermissionsManager {
/** /**
* Reload the permissions manager, and re-hook all permission plugins. * Reload the permissions manager, and re-hook all permission plugins.
*/ */
@Override
public void reload() { public void reload() {
// Unhook all permission plugins // Unhook all permission plugins
unhook(); unhook();
// Set up the permissions manager again, return the result // Set up the permissions manager again
setup(); setup();
} }
@ -216,6 +174,15 @@ public class PermissionsManager {
} }
} }
/**
* Return the permissions system that is hooked into.
*
* @return The permissions system, or null.
*/
public PermissionsSystemType getPermissionSystem() {
return isEnabled() ? handler.getPermissionSystem() : null;
}
/** /**
* Check if the command sender has permission for the given permissions node. If no permissions system is used or * Check if the command sender has permission for the given permissions node. If no permissions system is used or
* if the sender is not a player (e.g. console user), the player has to be OP in order to have the permission. * if the sender is not a player (e.g. console user), the player has to be OP in order to have the permission.
@ -240,6 +207,39 @@ public class PermissionsManager {
return handler.hasPermission(player, permissionNode); return handler.hasPermission(player, permissionNode);
} }
/**
* Check if a player has permission for the given permission node. This is for offline player checks. If no permissions
* system is used, then the player will not have permission.
*
* @param player The offline player
* @param permissionNode The permission node to verify
*
* @return true if the player has permission, false otherwise
*/
public boolean hasPermissionOffline(OfflinePlayer player, PermissionNode permissionNode) {
// Check if the permission node is null
if (permissionNode == null) {
return true;
}
if (!isEnabled()) {
return permissionNode.getDefaultPermission().evaluate(player);
}
return handler.hasPermissionOffline(player.getName(), permissionNode);
}
public boolean hasPermissionOffline(String name, PermissionNode permissionNode) {
if (permissionNode == null) {
return true;
}
if (!isEnabled()) {
return permissionNode.getDefaultPermission().evaluate(null);
}
return handler.hasPermissionOffline(name, permissionNode);
}
/** /**
* Check whether the current permissions system has group support. * Check whether the current permissions system has group support.
* If no permissions system is hooked, false will be returned. * If no permissions system is hooked, false will be returned.

View File

@ -24,7 +24,12 @@ public enum PlayerStatePermission implements PermissionNode {
/** /**
* Permission to be able to register multiple accounts. * Permission to be able to register multiple accounts.
*/ */
ALLOW_MULTIPLE_ACCOUNTS("authme.allowmultipleaccounts", DefaultPermission.OP_ONLY); ALLOW_MULTIPLE_ACCOUNTS("authme.allowmultipleaccounts", DefaultPermission.OP_ONLY),
/**
* Permission to bypass the purging process
*/
BYPASS_PURGE("authme.bypasspurge", DefaultPermission.NOT_ALLOWED);
/** /**
* The permission node. * The permission node.

View File

@ -27,6 +27,11 @@ public class BPermissionsHandler implements PermissionHandler {
return ApiLayer.hasPermission(player.getWorld().getName(), CalculableType.USER, player.getName(), node.getNode()); return ApiLayer.hasPermission(player.getWorld().getName(), CalculableType.USER, player.getName(), node.getNode());
} }
@Override
public boolean hasPermissionOffline(String name, PermissionNode node) {
return ApiLayer.hasPermission(null, CalculableType.USER, name, node.getNode());
}
@Override @Override
public boolean isInGroup(Player player, String group) { public boolean isInGroup(Player player, String group) {
return ApiLayer.hasGroup(player.getWorld().getName(), CalculableType.USER, player.getName(), group); return ApiLayer.hasGroup(player.getWorld().getName(), CalculableType.USER, player.getName(), group);

View File

@ -35,6 +35,17 @@ public class GroupManagerHandler implements PermissionHandler {
return handler != null && handler.has(player, node.getNode()); return handler != null && handler.has(player, node.getNode());
} }
@Override
public boolean hasPermissionOffline(String name, PermissionNode node) {
final AnjoPermissionsHandler handler = groupManager.getWorldsHolder().getWorldPermissionsByPlayerName(name);
if(handler == null) {
return false;
}
List<String> perms = handler.getAllPlayersPermissions(name);
return perms.contains(node.getNode());
}
@Override @Override
public boolean isInGroup(Player player, String group) { public boolean isInGroup(Player player, String group) {
final AnjoPermissionsHandler handler = groupManager.getWorldsHolder().getWorldPermissions(player); final AnjoPermissionsHandler handler = groupManager.getWorldsHolder().getWorldPermissions(player);

View File

@ -38,6 +38,17 @@ public interface PermissionHandler {
*/ */
boolean hasPermission(Player player, PermissionNode node); boolean hasPermission(Player player, PermissionNode node);
/**
* Check if a player has permission by their name.
* Used to check an offline player's permission.
*
* @param name The player's name.
* @param node The permission node.
*
* @return True if the player has permission.
*/
boolean hasPermissionOffline(String name, PermissionNode node);
/** /**
* Check whether the player is in the specified group. * Check whether the player is in the specified group.
* *

View File

@ -0,0 +1,12 @@
package fr.xephi.authme.permission.handlers;
/**
* Exception during the instantiation of a {@link PermissionHandler}.
*/
@SuppressWarnings("serial")
public class PermissionHandlerException extends Exception {
public PermissionHandlerException(String message) {
super(message);
}
}

View File

@ -25,6 +25,11 @@ public class PermissionsBukkitHandler implements PermissionHandler {
return player.hasPermission(node.getNode()); return player.hasPermission(node.getNode());
} }
@Override
public boolean hasPermissionOffline(String name, PermissionNode node) {
return false;
}
@Override @Override
public boolean isInGroup(Player player, String group) { public boolean isInGroup(Player player, String group) {
List<String> groupNames = getGroups(player); List<String> groupNames = getGroups(player);

View File

@ -1,6 +1,5 @@
package fr.xephi.authme.permission.handlers; package fr.xephi.authme.permission.handlers;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsSystemType; import fr.xephi.authme.permission.PermissionsSystemType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -15,14 +14,16 @@ public class PermissionsExHandler implements PermissionHandler {
private PermissionManager permissionManager; private PermissionManager permissionManager;
public PermissionsExHandler(PermissionManager permissionManager) { public PermissionsExHandler() throws PermissionHandlerException {
this.permissionManager = permissionManager; permissionManager = PermissionsEx.getPermissionManager();
if (permissionManager == null) {
throw new PermissionHandlerException("Could not get manager of PermissionsEx");
}
} }
@Override @Override
public boolean addToGroup(Player player, String group) { public boolean addToGroup(Player player, String group) {
if (!PermissionsEx.getPermissionManager().getGroupNames().contains(group)) { if (!PermissionsEx.getPermissionManager().getGroupNames().contains(group)) {
ConsoleLogger.showError("The plugin tried to set " + player + "'s group to '" + group + "', but it doesn't exist!");
return false; return false;
} }
@ -42,6 +43,12 @@ public class PermissionsExHandler implements PermissionHandler {
return user.has(node.getNode()); return user.has(node.getNode());
} }
@Override
public boolean hasPermissionOffline(String name, PermissionNode node) {
PermissionUser user = permissionManager.getUser(name);
return user.has(node.getNode());
}
@Override @Override
public boolean isInGroup(Player player, String group) { public boolean isInGroup(Player player, String group) {
PermissionUser user = permissionManager.getUser(player); PermissionUser user = permissionManager.getUser(player);

View File

@ -3,7 +3,9 @@ package fr.xephi.authme.permission.handlers;
import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsSystemType; import fr.xephi.authme.permission.PermissionsSystemType;
import net.milkbowl.vault.permission.Permission; import net.milkbowl.vault.permission.Permission;
import org.bukkit.Server;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.RegisteredServiceProvider;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -12,8 +14,24 @@ public class VaultHandler implements PermissionHandler {
private Permission vaultProvider; private Permission vaultProvider;
public VaultHandler(Permission vaultProvider) { public VaultHandler(Server server) throws PermissionHandlerException {
this.vaultProvider = vaultProvider; this.vaultProvider = getVaultPermission(server);
}
private static Permission getVaultPermission(Server server) throws PermissionHandlerException {
// Get the permissions provider service
RegisteredServiceProvider<Permission> permissionProvider = server
.getServicesManager().getRegistration(Permission.class);
if (permissionProvider == null) {
throw new PermissionHandlerException("Could not load permissions provider service");
}
// Get the Vault provider and make sure it's valid
Permission vaultPerms = permissionProvider.getProvider();
if (vaultPerms == null) {
throw new PermissionHandlerException("Could not load Vault permissions provider");
}
return vaultPerms;
} }
@Override @Override
@ -31,6 +49,11 @@ public class VaultHandler implements PermissionHandler {
return vaultProvider.has(player, node.getNode()); return vaultProvider.has(player, node.getNode());
} }
@Override
public boolean hasPermissionOffline(String name, PermissionNode node) {
return vaultProvider.has("", name, node.getNode());
}
@Override @Override
public boolean isInGroup(Player player, String group) { public boolean isInGroup(Player player, String group) {
return vaultProvider.playerInGroup(player, group); return vaultProvider.playerInGroup(player, group);

View File

@ -14,7 +14,12 @@ public class ZPermissionsHandler implements PermissionHandler {
private ZPermissionsService zPermissionsService; private ZPermissionsService zPermissionsService;
public ZPermissionsHandler(ZPermissionsService zPermissionsService) { public ZPermissionsHandler() throws PermissionHandlerException {
// Set the zPermissions service and make sure it's valid
ZPermissionsService zPermissionsService = Bukkit.getServicesManager().load(ZPermissionsService.class);
if (zPermissionsService == null) {
throw new PermissionHandlerException("Failed to get the ZPermissions service!");
}
this.zPermissionsService = zPermissionsService; this.zPermissionsService = zPermissionsService;
} }
@ -37,6 +42,15 @@ public class ZPermissionsHandler implements PermissionHandler {
return node.getDefaultPermission().evaluate(player); return node.getDefaultPermission().evaluate(player);
} }
@Override
public boolean hasPermissionOffline(String name, PermissionNode node) {
Map<String, Boolean> perms = zPermissionsService.getPlayerPermissions(null, null, name);
if (perms.containsKey(node.getNode()))
return perms.get(node.getNode());
else
return false;
}
@Override @Override
public boolean isInGroup(Player player, String group) { public boolean isInGroup(Player player, String group) {
return getGroups(player).contains(group); return getGroups(player).contains(group);

View File

@ -10,6 +10,7 @@ import fr.xephi.authme.process.quit.AsynchronousQuit;
import fr.xephi.authme.process.register.AsyncRegister; import fr.xephi.authme.process.register.AsyncRegister;
import fr.xephi.authme.process.unregister.AsynchronousUnregister; import fr.xephi.authme.process.unregister.AsynchronousUnregister;
import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.BukkitService;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import javax.inject.Inject; import javax.inject.Inject;
@ -70,11 +71,20 @@ public class Management {
}); });
} }
public void performUnregister(final Player player, final String password, final boolean isForce) { public void performUnregister(final Player player, final String password) {
runTask(new Runnable() { runTask(new Runnable() {
@Override @Override
public void run() { public void run() {
asynchronousUnregister.unregister(player, password, isForce); asynchronousUnregister.unregister(player, password);
}
});
}
public void performUnregisterByAdmin(final CommandSender initiator, final String name, final Player player) {
runTask(new Runnable() {
@Override
public void run() {
asynchronousUnregister.adminUnregister(initiator, name, player);
} }
}); });
} }
@ -88,11 +98,11 @@ public class Management {
}); });
} }
public void performQuit(final Player player, final boolean isKick) { public void performQuit(final Player player) {
runTask(new Runnable() { runTask(new Runnable() {
@Override @Override
public void run() { public void run() {
asynchronousQuit.processQuit(player, isKick); asynchronousQuit.processQuit(player);
} }
}); });
} }

Some files were not shown because too many files have changed in this diff Show More