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>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</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>
</option>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Project" />
</component>
</project>

View File

@ -2,23 +2,23 @@
<p align="center"><strong>The most used authentication plugin for CraftBukkit/Spigot!</strong></p>
<hr>
#####News:
- Latest version of AuthMeBridge is finally compatible with latest AuthMe snapshots! ;)
#####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)
- 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)
- 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)
- Dependencies: [![Dependency Status](https://www.versioneye.com/user/projects/5617ca36a193340f28000222/badge.svg)](https://www.versioneye.com/user/projects/5617ca36a193340f28000222)
- 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)
@ -39,6 +39,7 @@ McStats: http://mcstats.org/plugin/AuthMe
<img src="http://i.mcstats.org/AuthMe/Version+Demographics.borderless.png">
#####Development history:
Outdated!
[![Gource AuthMe History Video](http://img.youtube.com/vi/hJRzNfYyd9k/hqdefault.jpg)](https://www.youtube.com/watch?v=hJRzNfYyd9k)
<hr>
@ -53,8 +54,8 @@ McStats: http://mcstats.org/plugin/AuthMe
>- Execute command "mvn clean install"
#####Running Requirements:
>- Java 1.7 (should work also with Java 1.8)
>- PaperSpigot, Spigot or CraftBukkit (1.7.10, 1.8.X or 1.9.X)
>- Java 1.7 or 1.8
>- PaperSpigot, Spigot or CraftBukkit (1.7.10, 1.8.X, 1.9.X, 1.10.X)
>- ProtocolLib (optional, required by the protectInventory feature)
<hr>
@ -62,13 +63,16 @@ McStats: http://mcstats.org/plugin/AuthMe
#####"The best authentication plugin for the Bukkit/Spigot API!"
<p>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>
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>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>
<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>
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.
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:
<ul>
@ -114,24 +118,25 @@ typing commands or using the inventory. It can also kick players with uncommonly
####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>
####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
<ul><li>authme.player.* - for all user commands
</li><li>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>
</li></ul>
- authme.player.* - for all user commands
- authme.admin.* - for all admin commands
- [List of all permission nodes](http://github.com/AuthMe-Team/AuthMeReloaded/blob/master/docs/permission_nodes.md)
####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>
</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>
</li><li><a href="http://dev.bukkit.org/server-mods/authme-reloaded/pages/web-site-integration/">Website Integration</a>
</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>
</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>
</li><li>Convert from FlatFile (auths.db but not the sqlite one) to MySQL: /authme converter
</li></ul>
- [How to install and set up](http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-install-and-initial-configuration/)
- [How to import database from xAuth](http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-import-database-from-xauth/)
- [Website integration](http://dev.bukkit.org/server-mods/authme-reloaded/pages/web-site-integration/)
- [Click here for an example of the config file](https://raw.githubusercontent.com/Xephi/AuthMeReloaded/master/src/main/resources/config.yml)
- [How to convert from Rakamak](http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-import-database-from-rakamak/)
- Convert from FlatFile (auths.db but not the sqlite one) to MySQL: /authme converter
<hr>
#####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>
#####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)
#####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>Thanks also to: AS1LV3RN1NJA, Hoeze and eprimex</p>

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@
* ------------------------------------------------------- *
* See AuthMeController for details. *
* *
* Source: https://github.com/AuthMe-Team/AuthMeReloaded/ *
* Source: https://github.com/AuthMe/AuthMeReloaded/ *
***********************************************************/
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.permission.PermissionsManager;
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.util.BukkitService;
import org.bukkit.entity.Player;
@ -20,16 +20,16 @@ import static fr.xephi.authme.util.BukkitService.TICKS_PER_SECOND;
*/
public class AntiBot {
private final NewSetting settings;
private final Settings settings;
private final Messages messages;
private final PermissionsManager permissionsManager;
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 AntiBotStatus antiBotStatus = AntiBotStatus.DISABLED;
@Inject
AntiBot(NewSetting settings, Messages messages, PermissionsManager permissionsManager,
AntiBot(Settings settings, Messages messages, PermissionsManager permissionsManager,
BukkitService bukkitService) {
this.settings = settings;
this.messages = messages;
@ -112,6 +112,27 @@ public class AntiBot {
}, 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 {
LISTENING,
DISABLED,

View File

@ -1,146 +1,95 @@
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.NewAPI;
import fr.xephi.authme.cache.auth.PlayerAuth;
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.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.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.MetricsStarter;
import fr.xephi.authme.listener.AuthMeBlockListener;
import fr.xephi.authme.listener.AuthMeEntityListener;
import fr.xephi.authme.listener.AuthMeInventoryPacketAdapter;
import fr.xephi.authme.listener.AuthMePlayerListener;
import fr.xephi.authme.listener.AuthMePlayerListener16;
import fr.xephi.authme.listener.AuthMePlayerListener18;
import fr.xephi.authme.listener.AuthMeServerListener;
import fr.xephi.authme.listener.AuthMeTabCompletePacketAdapter;
import fr.xephi.authme.listener.AuthMeTablistPacketAdapter;
import fr.xephi.authme.mail.SendMailSSL;
import fr.xephi.authme.output.ConsoleFilter;
import fr.xephi.authme.output.Log4JFilter;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.initialization.Initializer;
import fr.xephi.authme.initialization.MetricsManager;
import fr.xephi.authme.initialization.OnShutdownPlayerSaver;
import fr.xephi.authme.initialization.TaskCloser;
import fr.xephi.authme.listener.BlockListener;
import fr.xephi.authme.listener.EntityListener;
import fr.xephi.authme.listener.PlayerListener;
import fr.xephi.authme.listener.PlayerListener16;
import fr.xephi.authme.listener.PlayerListener18;
import fr.xephi.authme.listener.PlayerListener19;
import fr.xephi.authme.listener.ServerListener;
import fr.xephi.authme.output.Messages;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.permission.PermissionsSystemType;
import fr.xephi.authme.security.crypts.SHA256;
import fr.xephi.authme.settings.NewSetting;
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.PurgeSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
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.task.PurgeTask;
import fr.xephi.authme.task.CleanupTask;
import fr.xephi.authme.task.purge.PurgeService;
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.MigrationService;
import fr.xephi.authme.util.StringUtils;
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.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginLoader;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
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.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.settings.properties.EmailSettings.MAIL_PASSWORD;
import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS;
import static fr.xephi.authme.util.BukkitService.TICKS_PER_MINUTE;
import static fr.xephi.authme.util.Utils.isClassLoaded;
/**
* The AuthMe main class.
*/
public class AuthMe extends JavaPlugin {
// Defines the name of the plugin.
// Constants
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;
private static String pluginVersion = "N/D";
private static String pluginBuildNumber = "Unknown";
// 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 instances
private CommandHandler commandHandler;
private PermissionsManager permsMan;
private NewSetting newSettings;
private Settings settings;
private Messages messages;
private JsonCache playerBackup;
private PasswordSecurity passwordSecurity;
private DataSource database;
private PluginHooks pluginHooks;
private SpawnLoader spawnLoader;
private boolean autoPurging;
private BukkitService bukkitService;
private AuthMeServiceInitializer initializer;
private Injector injector;
private GeoLiteAPI geoLiteApi;
private PlayerCache playerCache;
/**
* Get the plugin's instance.
*
* @return AuthMe
* Constructor.
*/
@Deprecated
public static AuthMe getInstance() {
return plugin;
public AuthMe() {
}
/*
* 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;
}
/**
* 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
private void setPluginInfos() {
private void loadPluginInfo() {
String versionRaw = this.getDescription().getVersion();
int index = versionRaw.lastIndexOf("-");
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
public void onEnable() {
// Set various instances
plugin = this;
ConsoleLogger.setLogger(getLogger());
setPluginInfos();
protected void instantiateServices(Injector injector) {
// PlayerCache is still injected statically sometimes
playerCache = PlayerCache.getInstance();
injector.register(PlayerCache.class, playerCache);
// Load settings and custom configurations, if it fails, stop the server due to security reasons.
newSettings = createNewSetting();
if (newSettings == null) {
getLogger().warning("Could not load configuration. Aborting.");
getServer().shutdown();
return;
}
ConsoleLogger.setLogFile(new File(getDataFolder(), "authme.log"));
ConsoleLogger.setLoggingOptions(newSettings);
messages = injector.getSingleton(Messages.class);
permsMan = injector.getSingleton(PermissionsManager.class);
bukkitService = injector.getSingleton(BukkitService.class);
commandHandler = injector.getSingleton(CommandHandler.class);
geoLiteApi = injector.getSingleton(GeoLiteAPI.class);
// Old settings manager
if (!loadSettings()) {
getServer().shutdown();
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);
}
// Trigger construction of API classes; they will keep track of the singleton
injector.getSingleton(NewAPI.class);
injector.getSingleton(API.class);
}
/**
@ -321,370 +250,105 @@ public class AuthMe extends JavaPlugin {
*/
private void showSettingsWarnings() {
// Force single session disabled
if (!newSettings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)) {
ConsoleLogger.showError("WARNING!!! By disabling ForceSingleSession, your server protection is inadequate!");
if (!settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)) {
ConsoleLogger.warning("WARNING!!! By disabling ForceSingleSession, your server protection is inadequate!");
}
// Session timeout disabled
if (newSettings.getProperty(PluginSettings.SESSIONS_TIMEOUT) == 0
&& newSettings.getProperty(PluginSettings.SESSIONS_ENABLED)) {
ConsoleLogger.showError("WARNING!!! You set session timeout to 0, this may cause security issues!");
if (settings.getProperty(PluginSettings.SESSIONS_TIMEOUT) == 0
&& settings.getProperty(PluginSettings.SESSIONS_ENABLED)) {
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
PluginManager pluginManager = getServer().getPluginManager();
// Register event listeners
pluginManager.registerEvents(initializer.get(AuthMePlayerListener.class), this);
pluginManager.registerEvents(initializer.get(AuthMeBlockListener.class), this);
pluginManager.registerEvents(initializer.get(AuthMeEntityListener.class), this);
pluginManager.registerEvents(initializer.get(AuthMeServerListener.class), this);
pluginManager.registerEvents(injector.getSingleton(PlayerListener.class), this);
pluginManager.registerEvents(injector.getSingleton(BlockListener.class), this);
pluginManager.registerEvents(injector.getSingleton(EntityListener.class), this);
pluginManager.registerEvents(injector.getSingleton(ServerListener.class), this);
// Try to register 1.6 player listeners
try {
Class.forName("org.bukkit.event.player.PlayerEditBookEvent");
pluginManager.registerEvents(initializer.get(AuthMePlayerListener16.class), this);
} catch (ClassNotFoundException ignore) {
if (isClassLoaded("org.bukkit.event.player.PlayerEditBookEvent")) {
pluginManager.registerEvents(injector.getSingleton(PlayerListener16.class), this);
}
// Try to register 1.8 player listeners
try {
Class.forName("org.bukkit.event.player.PlayerInteractAtEntityEvent");
pluginManager.registerEvents(initializer.get(AuthMePlayerListener18.class), this);
} catch (ClassNotFoundException ignore) {
if (isClassLoaded("org.bukkit.event.player.PlayerInteractAtEntityEvent")) {
pluginManager.registerEvents(injector.getSingleton(PlayerListener18.class), this);
}
// 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() {
if (database != null) {
int playersOnline = bukkitService.getOnlinePlayers().size();
if (playersOnline < 1) {
if (playersOnline == 0) {
database.purgeLogged();
} else if (Settings.reloadSupport) {
} else if (settings.getProperty(SecuritySettings.USE_RELOAD_COMMAND_SUPPORT)) {
for (PlayerAuth auth : database.getLoggedPlayers()) {
if (auth == null) {
continue;
if (auth != null) {
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
public void onDisable() {
// Save player data
BukkitService bukkitService = initializer.getIfAvailable(BukkitService.class);
LimboCache limboCache = initializer.getIfAvailable(LimboCache.class);
if (bukkitService != null && limboCache != null) {
Collection<? extends Player> players = bukkitService.getOnlinePlayers();
for (Player player : players) {
savePlayer(player, limboCache);
}
// onDisable is also called when we prematurely abort, so any field may be null
OnShutdownPlayerSaver onShutdownPlayerSaver = injector == null
? null
: injector.createIfHasDependencies(OnShutdownPlayerSaver.class);
if (onShutdownPlayerSaver != null) {
onShutdownPlayerSaver.saveAllPlayers();
}
// Do backup on stop if enabled
if (newSettings != null) {
new PerformBackup(this, newSettings).doBackup(PerformBackup.BackupCause.STOP);
if (settings != null) {
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++;
getLogger().info("Progress: " + progress + " / " + pendingTasks.size());
}
if (database != null) {
database.close();
}
}
}, "AuthMe-DataSource#close").start();
// Wait for tasks and close data source
new Thread(
new TaskCloser(this, database),
"AuthMe-DataSource#close"
).start();
// Disabled correctly
ConsoleLogger.info("AuthMe " + this.getDescription().getVersion() + " disabled!");
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) {
String playersOnline = Integer.toString(bukkitService.getOnlinePlayers().size());
String ipAddress = Utils.getPlayerIp(player);
@ -695,15 +359,15 @@ public class AuthMe extends JavaPlugin {
.replace("{ONLINE}", playersOnline)
.replace("{MAXPLAYERS}", Integer.toString(server.getMaxPlayers()))
.replace("{IP}", ipAddress)
.replace("{LOGINS}", Integer.toString(PlayerCache.getInstance().getLogged()))
.replace("{LOGINS}", Integer.toString(playerCache.getLogged()))
.replace("{WORLD}", player.getWorld().getName())
.replace("{SERVER}", server.getServerName())
.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.
*
@ -726,67 +390,4 @@ public class AuthMe extends JavaPlugin {
// Handle the command
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;
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.util.StringUtils;
@ -11,7 +13,6 @@ import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;
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 DateFormat DATE_FORMAT = new SimpleDateFormat("[MM-dd HH:mm:ss]");
private static Logger logger;
private static boolean enableDebug = false;
private static LogLevel logLevel = LogLevel.INFO;
private static boolean useLogging = false;
private static File logFile;
private static FileWriter fileWriter;
@ -30,17 +31,36 @@ public final class ConsoleLogger {
private ConsoleLogger() {
}
// --------
// Configurations
// --------
/**
* Set the logger to use.
*
* @param logger The logger
*/
public static void setLogger(Logger logger) {
ConsoleLogger.logger = logger;
}
/**
* Set the file to log to if enabled.
*
* @param logFile The log file
*/
public static void setLogFile(File 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.enableDebug = !settings.getProperty(SecuritySettings.REMOVE_SPAM_FROM_CONSOLE);
if (useLogging) {
if (fileWriter == null) {
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) {
logger.info(message);
if (useLogging) {
writeLog(message);
}
writeLog("[INFO] " + 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) {
if (enableDebug) {
//creating and filling an exception is a expensive call
//TODO #419 20160601: ->so it should be removed as soon #419 is fixed
//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);
}
if (logLevel.includes(LogLevel.DEBUG)) {
logger.info("Debug: " + message);
writeLog("[DEBUG] " + message);
}
}
/**
* Print an error message.
*
* @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.
* Log a Throwable with the provided message on WARNING level
* and save the stack trace to the log file.
*
* @param message The message to accompany the exception
* @param th The Throwable to log
*/
public static void logException(String message, Throwable th) {
showError(message + " " + StringUtils.formatException(th));
writeStackTrace(th);
warning(message + " " + StringUtils.formatException(th));
writeLog(Throwables.getStackTraceAsString(th));
}
// --------
// Helpers
// --------
/**
* Close all file handles.
*/
public static void close() {
if (fileWriter != null) {
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;
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.DatabaseSettings;
import fr.xephi.authme.util.StringUtils;
@ -30,7 +30,7 @@ public class PerformBackup {
private final String tblname;
private final String path;
private final File dataFolder;
private final NewSetting settings;
private final Settings settings;
/**
* Constructor for PerformBackup.
@ -38,7 +38,7 @@ public class PerformBackup {
* @param instance AuthMe
* @param settings The plugin settings
*/
public PerformBackup(AuthMe instance, NewSetting settings) {
public PerformBackup(AuthMe instance, Settings settings) {
this.dataFolder = instance.getDataFolder();
this.settings = settings;
this.dbName = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
@ -60,7 +60,7 @@ public class PerformBackup {
if (!settings.getProperty(BackupSettings.ENABLED)) {
// Print a warning if the backup was requested via command or by another plugin
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());
}
return;
@ -76,7 +76,7 @@ public class PerformBackup {
if (doBackup()) {
ConsoleLogger.info("A backup has been performed successfully. Cause of the Backup: " + cause.name());
} 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:
return fileBackup(dbName + ".db");
default:
ConsoleLogger.showError("Unknown data source type '" + dataSourceType + "' for backup");
ConsoleLogger.warning("Unknown data source type '" + dataSourceType + "' for backup");
}
return false;
@ -113,7 +113,7 @@ public class PerformBackup {
ConsoleLogger.info("Backup created successfully.");
return true;
} else {
ConsoleLogger.showError("Could not create the backup! (Windows)");
ConsoleLogger.warning("Could not create the backup! (Windows)");
}
} catch (IOException | InterruptedException e) {
ConsoleLogger.logException("Error during Windows backup:", e);
@ -128,7 +128,7 @@ public class PerformBackup {
ConsoleLogger.info("Backup created successfully.");
return true;
} else {
ConsoleLogger.showError("Could not create the backup!");
ConsoleLogger.warning("Could not create the backup!");
}
} catch (IOException | InterruptedException e) {
ConsoleLogger.logException("Error during backup:", e);
@ -147,8 +147,7 @@ public class PerformBackup {
copy("plugins" + File.separator + "AuthMe" + File.separator + backend, path + ".db");
return true;
} catch (IOException ex) {
ConsoleLogger.showError("Encountered an error during file backup: " + StringUtils.formatException(ex));
ConsoleLogger.writeStackTrace(ex);
ConsoleLogger.logException("Encountered an error during file backup:", ex);
}
return false;
}
@ -166,7 +165,7 @@ public class PerformBackup {
if (new File(windowsPath + "\\bin\\mysqldump.exe").exists()) {
return true;
} else {
ConsoleLogger.showError("Mysql Windows Path is incorrect. Please check it");
ConsoleLogger.warning("Mysql Windows Path is incorrect. Please check it");
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.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.util.Utils;
import fr.xephi.authme.util.ValidationService;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@ -27,19 +28,21 @@ public class API {
private static DataSource dataSource;
private static PasswordSecurity passwordSecurity;
private static Management management;
private static PluginHooks pluginHooks;
private static ValidationService validationService;
/**
* Constructor for the deprecated API.
*
* @param instance AuthMe
/*
* Constructor.
*/
@Deprecated
@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.dataSource = dataSource;
API.passwordSecurity = passwordSecurity;
API.management = management;
API.pluginHooks = pluginHooks;
API.validationService = validationService;
}
/**
@ -47,7 +50,6 @@ public class API {
*
* @return AuthMe instance
*/
@Deprecated
public static AuthMe hookAuthMe() {
if (instance != null) {
return instance;
@ -66,7 +68,6 @@ public class API {
* @param player The player to verify
* @return true if the player is authenticated
*/
@Deprecated
public static boolean isAuthenticated(Player player) {
return PlayerCache.getInstance().isAuthenticated(player.getName());
}
@ -77,12 +78,10 @@ public class API {
* @param player The player to verify
* @return true if the player is unrestricted
*/
@Deprecated
public static boolean isUnrestricted(Player player) {
return Utils.isUnrestricted(player);
return validationService.isUnrestricted(player.getName());
}
@Deprecated
public static Location getLastLocation(Player player) {
try {
PlayerAuth auth = PlayerCache.getInstance().getAuth(player.getName().toLowerCase());
@ -99,7 +98,6 @@ public class API {
}
}
@Deprecated
public static void setPlayerInventory(Player player, ItemStack[] content,
ItemStack[] armor) {
try {
@ -115,7 +113,6 @@ public class API {
* @param playerName The player name to verify
* @return true if player is registered
*/
@Deprecated
public static boolean isRegistered(String playerName) {
String player = playerName.toLowerCase();
return dataSource.isAuthAvailable(player);
@ -128,7 +125,6 @@ public class API {
* @param passwordToCheck The password to check
* @return true if the password is correct, false otherwise
*/
@Deprecated
public static boolean checkPassword(String playerName, String passwordToCheck) {
return isRegistered(playerName) && passwordSecurity.comparePassword(passwordToCheck, playerName);
}
@ -140,7 +136,6 @@ public class API {
* @param password The password
* @return true if the player was registered correctly
*/
@Deprecated
public static boolean registerPlayer(String playerName, String password) {
String name = playerName.toLowerCase();
HashedPassword hashedPassword = passwordSecurity.computeHash(password, name);
@ -161,12 +156,10 @@ public class API {
*
* @param player The player to log in
*/
@Deprecated
public static void forceLogin(Player player) {
management.performLogin(player, "dontneed", true);
}
@Deprecated
public AuthMe getPlugin() {
return instance;
}
@ -177,9 +170,8 @@ public class API {
* @param player The player to verify
* @return true if player is an npc
*/
@Deprecated
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.cache.auth.PlayerAuth;
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.util.Utils;
import fr.xephi.authme.util.ValidationService;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import javax.inject.Inject;
@ -22,34 +25,41 @@ public class NewAPI {
public static NewAPI singleton;
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.
*
* @param plugin The AuthMe plugin instance
*/
@Inject
public NewAPI(AuthMe plugin) {
NewAPI(AuthMe plugin, PluginHooks pluginHooks, DataSource dataSource, PasswordSecurity passwordSecurity,
Management management, ValidationService validationService, PlayerCache playerCache) {
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.
*
* @return The API object, or null if the AuthMe plugin instance could not be retrieved
* from the server environment
* @return The API object, or null if the AuthMe plugin is not enabled or not fully initialized yet
*/
public static NewAPI getInstance() {
if (singleton != null) {
return singleton;
}
Plugin p = Bukkit.getServer().getPluginManager().getPlugin("AuthMe");
if (p == null || !(p instanceof AuthMe)) {
return null;
}
AuthMe authme = (AuthMe) p;
singleton = new NewAPI(authme);
return singleton;
// NewAPI is initialized in AuthMe#onEnable -> if singleton is null,
// it means AuthMe isn't initialized (yet)
return null;
}
/**
@ -78,7 +88,7 @@ public class NewAPI {
* @return true if the player is authenticated
*/
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
*/
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
*/
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
* @return Location The location of the player
*/
public Location getLastLocation(Player player) {
PlayerAuth auth = PlayerCache.getInstance().getAuth(player.getName());
PlayerAuth auth = playerCache.getAuth(player.getName());
if (auth != null) {
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) {
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
*/
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) {
String name = playerName.toLowerCase();
HashedPassword result = plugin.getPasswordSecurity().computeHash(password, name);
HashedPassword result = passwordSecurity.computeHash(password, name);
if (isRegistered(name)) {
return false;
}
@ -158,7 +168,7 @@ public class NewAPI {
.password(result)
.realName(playerName)
.build();
return plugin.getDataSource().saveAuth(auth);
return dataSource.saveAuth(auth);
}
/**
@ -167,7 +177,7 @@ public class NewAPI {
* @param player The player to log in
*/
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
*/
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?
*/
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
*/
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.security.RandomString;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import javax.inject.Inject;
@ -21,10 +21,10 @@ public class CaptchaManager implements SettingsDependent {
private int captchaLength;
@Inject
CaptchaManager(NewSetting settings) {
CaptchaManager(Settings settings) {
this.playerCounts = new ConcurrentHashMap<>();
this.captchaCodes = new ConcurrentHashMap<>();
loadSettings(settings);
reload(settings);
}
/**
@ -123,7 +123,7 @@ public class CaptchaManager implements SettingsDependent {
}
@Override
public void loadSettings(NewSetting settings) {
public void reload(Settings settings) {
this.isEnabled = settings.getProperty(SecuritySettings.USE_CAPTCHA);
this.threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TRIES_BEFORE_CAPTCHA);
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.output.MessageKey;
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.util.BukkitService;
import fr.xephi.authme.util.Utils;
@ -30,11 +30,11 @@ public class TempbanManager implements SettingsDependent {
private int length;
@Inject
TempbanManager(BukkitService bukkitService, Messages messages, NewSetting settings) {
TempbanManager(BukkitService bukkitService, Messages messages, Settings settings) {
this.ipLoginFailureCounts = new ConcurrentHashMap<>();
this.bukkitService = bukkitService;
this.messages = messages;
loadSettings(settings);
reload(settings);
}
/**
@ -108,7 +108,7 @@ public class TempbanManager implements SettingsDependent {
}
@Override
public void loadSettings(NewSetting settings) {
public void reload(Settings settings) {
this.isEnabled = settings.getProperty(SecuritySettings.TEMPBAN_ON_MAX_LOGINS);
this.threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TEMPBAN);
this.length = settings.getProperty(SecuritySettings.TEMPBAN_LENGTH);

View File

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

View File

@ -1,10 +1,10 @@
package fr.xephi.authme.cache.auth;
import fr.xephi.authme.ConsoleLogger;
import java.util.concurrent.ConcurrentHashMap;
/**
* Used to manage player's Authenticated status
*/
public class PlayerCache {
@ -34,7 +34,6 @@ public class PlayerCache {
* @param auth PlayerAuth
*/
public void addPlayer(PlayerAuth auth) {
ConsoleLogger.debug("ADDED PLAYER TO CACHE " + auth.getNickname());
cache.put(auth.getNickname().toLowerCase(), auth);
}
@ -44,7 +43,6 @@ public class PlayerCache {
* @param auth PlayerAuth
*/
public void updatePlayer(PlayerAuth auth) {
ConsoleLogger.debug("UPDATE PLAYER " + auth.getNickname());
cache.put(auth.getNickname(), auth);
}
@ -54,16 +52,15 @@ public class PlayerCache {
* @param user String
*/
public void removePlayer(String user) {
ConsoleLogger.debug("REMOVE PLAYER " + user);
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) {
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;
import fr.xephi.authme.cache.backup.JsonCache;
import fr.xephi.authme.cache.backup.PlayerData;
import fr.xephi.authme.cache.backup.PlayerDataStorage;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.Settings;
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.entity.Player;
import javax.inject.Inject;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Manages all {@link LimboPlayer} instances.
* Manages all {@link PlayerData} instances.
*/
public class LimboCache {
private final ConcurrentHashMap<String, LimboPlayer> cache = new ConcurrentHashMap<>();
private final JsonCache jsonCache = new JsonCache();
private final Map<String, PlayerData> cache = new ConcurrentHashMap<>();
@Inject
private PlayerDataStorage playerDataStorage;
private Settings settings;
private PermissionsManager permissionsManager;
@Inject
private SpawnLoader spawnLoader;
@Inject
LimboCache(PermissionsManager permissionsManager, SpawnLoader spawnLoader) {
LimboCache(Settings settings, PermissionsManager permissionsManager,
SpawnLoader spawnLoader, PlayerDataStorage playerDataStorage) {
this.settings = settings;
this.permissionsManager = permissionsManager;
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.
*/
public void addLimboPlayer(Player player) {
public void addPlayerData(Player player) {
String name = player.getName().toLowerCase();
Location location = player.isDead() ? spawnLoader.getSpawnLocation(player) : player.getLocation();
Location location = spawnLoader.getPlayerLocationOrSpawn(player);
boolean operator = player.isOp();
boolean flyEnabled = player.getAllowFlight();
float walkSpeed = player.getWalkSpeed();
float flySpeed = player.getFlySpeed();
String playerGroup = "";
if (permissionsManager.hasGroupSupport()) {
playerGroup = permissionsManager.getPrimaryGroup(player);
}
if (jsonCache.doesCacheExist(player)) {
PlayerData cache = jsonCache.readCache(player);
if (playerDataStorage.hasData(player)) {
PlayerData cache = playerDataStorage.readData(player);
if (cache != null) {
location = cache.getLocation();
playerGroup = cache.getGroup();
operator = cache.getOperator();
flyEnabled = cache.isFlyEnabled();
operator = cache.isOperator();
flyEnabled = cache.isCanFly();
walkSpeed = cache.getWalkSpeed();
flySpeed = cache.getFlySpeed();
}
} else {
playerDataStorage.saveData(player);
}
cache.put(name, new LimboPlayer(name, location, operator, playerGroup, flyEnabled));
cache.put(name, new PlayerData(location, operator, playerGroup, flyEnabled, walkSpeed, flySpeed));
}
/**
* Method deleteLimboPlayer.
* Restore player's data to player if exist.
*
* @param name String
* @param player Player instance to restore
*/
public void deleteLimboPlayer(String name) {
checkNotNull(name);
name = name.toLowerCase();
LimboPlayer cachedPlayer = cache.remove(name);
public void restoreData(Player player) {
String lowerName = player.getName().toLowerCase();
if (cache.containsKey(lowerName)) {
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) {
cachedPlayer.clearTasks();
}
}
/**
* Method getLimboPlayer.
* Method getPlayerData.
*
* @param name String
*
* @return LimboPlayer
* @return PlayerData
*/
public LimboPlayer getLimboPlayer(String name) {
public PlayerData getPlayerData(String name) {
checkNotNull(name);
return cache.get(name.toLowerCase());
}
/**
* Method hasLimboPlayer.
* Method hasPlayerData.
*
* @param name String
*
* @return boolean
*/
public boolean hasLimboPlayer(String name) {
public boolean hasPlayerData(String name) {
checkNotNull(name);
return cache.containsKey(name.toLowerCase());
}
/**
* Method updateLimboPlayer.
* Method updatePlayerData.
*
* @param player Player
*/
public void updateLimboPlayer(Player player) {
public void updatePlayerData(Player player) {
checkNotNull(player);
deleteLimboPlayer(player.getName().toLowerCase());
addLimboPlayer(player);
removeFromCache(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)
* 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 fly;
private Location loc = null;
private final boolean canFly;
private final boolean operator;
private final String group;
private final Location loc;
private final float walkSpeed;
private final float flySpeed;
private BukkitTask timeoutTask = null;
private BukkitTask messageTask = null;
private boolean operator = false;
private String group;
public LimboPlayer(String name, Location loc, boolean operator,
String group, boolean fly) {
this.name = name;
public PlayerData(Location loc, boolean operator,
String group, boolean fly, float walkSpeed, float flySpeed) {
this.loc = loc;
this.operator = operator;
this.group = group;
this.fly = fly;
}
/**
* Return the name of the player.
*
* @return The player's name
*/
public String getName() {
return name;
this.canFly = fly;
this.walkSpeed = walkSpeed;
this.flySpeed = flySpeed;
}
/**
@ -40,7 +33,7 @@ public class LimboPlayer {
*
* @return The player's location
*/
public Location getLoc() {
public Location getLocation() {
return loc;
}
@ -62,8 +55,16 @@ public class LimboPlayer {
return group;
}
public boolean isFly() {
return fly;
public boolean isCanFly() {
return canFly;
}
public float getWalkSpeed() {
return walkSpeed;
}
public float getFlySpeed() {
return flySpeed;
}
/**

View File

@ -1,8 +1,8 @@
package fr.xephi.authme.command;
import ch.jalu.injector.Injector;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.command.help.HelpProvider;
import fr.xephi.authme.initialization.AuthMeServiceInitializer;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.ChatColor;
@ -37,12 +37,12 @@ public class CommandHandler {
private Map<Class<? extends ExecutableCommand>, ExecutableCommand> commands = new HashMap<>();
@Inject
public CommandHandler(AuthMeServiceInitializer initializer, CommandMapper commandMapper,
public CommandHandler(Injector injector, CommandMapper commandMapper,
PermissionsManager permissionsManager, HelpProvider helpProvider) {
this.commandMapper = commandMapper;
this.permissionsManager = permissionsManager;
this.helpProvider = helpProvider;
initializeCommands(initializer, commandMapper.getCommandClasses());
initializeCommands(injector, commandMapper.getCommandClasses());
}
/**
@ -90,12 +90,13 @@ public class CommandHandler {
/**
* Initialize all required ExecutableCommand objects.
*
* @param injector the injector
* @param commandClasses the classes to instantiate
*/
private void initializeCommands(AuthMeServiceInitializer initializer,
private void initializeCommands(Injector injector,
Set<Class<? extends ExecutableCommand>> 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.Messages;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.util.ValidationService;
import org.bukkit.command.CommandSender;
@ -18,7 +18,7 @@ public class CommandService {
@Inject
private Messages messages;
@Inject
private NewSetting settings;
private Settings settings;
@Inject
private ValidationService validationService;
@ -53,6 +53,16 @@ public class CommandService {
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.
*
@ -69,7 +79,7 @@ public class CommandService {
*
* @return The settings manager
*/
public NewSetting getSettings() {
public Settings getSettings() {
return settings;
}

View File

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

View File

@ -1,6 +1,8 @@
package fr.xephi.authme.command.executable.authme;
import ch.jalu.injector.Injector;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.ExecutableCommand;
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.vAuthConverter;
import fr.xephi.authme.converter.xAuthConverter;
import fr.xephi.authme.initialization.AuthMeServiceInitializer;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.util.BukkitService;
import org.bukkit.command.CommandSender;
@ -30,7 +31,7 @@ public class ConverterCommand implements ExecutableCommand {
private BukkitService bukkitService;
@Inject
private AuthMeServiceInitializer initializer;
private Injector injector;
@Override
public void executeCommand(final CommandSender sender, List<String> arguments) {
@ -45,13 +46,17 @@ public class ConverterCommand implements ExecutableCommand {
}
// Get the proper converter instance
final Converter converter = initializer.newInstance(jobType.getConverterClass());
final Converter converter = injector.newInstance(jobType.getConverterClass());
// Run the convert job
bukkitService.runTaskAsynchronously(new Runnable() {
@Override
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;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.task.PurgeTask;
import fr.xephi.authme.task.purge.PurgeService;
import fr.xephi.authme.util.BukkitService;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
@ -21,10 +18,7 @@ import java.util.Set;
public class PurgeBannedPlayersCommand implements ExecutableCommand {
@Inject
private DataSource dataSource;
@Inject
private AuthMe plugin;
private PurgeService purgeService;
@Inject
private BukkitService bukkitService;
@ -32,18 +26,12 @@ public class PurgeBannedPlayersCommand implements ExecutableCommand {
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
// Get the list of banned players
Set<String> namedBanned = new HashSet<>();
Set<OfflinePlayer> bannedPlayers = bukkitService.getBannedPlayers();
Set<String> namedBanned = new HashSet<>(bannedPlayers.size());
for (OfflinePlayer offlinePlayer : bannedPlayers) {
namedBanned.add(offlinePlayer.getName().toLowerCase());
}
//todo: note this should may run async because it may executes a SQL-Query
// 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);
purgeService.purgePlayers(sender, namedBanned, bannedPlayers.toArray(new OfflinePlayer[bannedPlayers.size()]));
}
}

View File

@ -1,16 +1,13 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.task.PurgeTask;
import fr.xephi.authme.task.purge.PurgeService;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.Calendar;
import java.util.List;
import java.util.Set;
/**
* 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;
@Inject
private DataSource dataSource;
@Inject
private AuthMe plugin;
private PurgeService purgeService;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
@ -52,13 +46,7 @@ public class PurgeCommand implements ExecutableCommand {
calendar.add(Calendar.DATE, -days);
long until = calendar.getTimeInMillis();
//todo: note this should may run async because it may executes a SQL-Query
// Purge the data, get the purged values
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);
// Run the purge
purgeService.runPurge(sender, until);
}
}

View File

@ -2,6 +2,7 @@ package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.ConsoleLogger;
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.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource;
@ -37,6 +38,9 @@ public class RegisterAdminCommand implements ExecutableCommand {
@Inject
private ValidationService validationService;
@Inject
private LimboCache limboCache;
@Override
public void executeCommand(final CommandSender sender, List<String> arguments) {
// Get the player name and password
@ -79,7 +83,8 @@ public class RegisterAdminCommand implements ExecutableCommand {
bukkitService.scheduleSyncDelayedTask(new Runnable() {
@Override
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;
import ch.jalu.injector.Injector;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.ExecutableCommand;
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.settings.NewSetting;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.Collection;
import java.util.List;
/**
@ -23,10 +26,10 @@ public class ReloadCommand implements ExecutableCommand {
private AuthMe plugin;
@Inject
private AuthMeServiceInitializer initializer;
private Injector injector;
@Inject
private NewSetting settings;
private Settings settings;
@Inject
private DataSource dataSource;
@ -44,7 +47,7 @@ public class ReloadCommand implements ExecutableCommand {
ConsoleLogger.info("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);
} catch (Exception e) {
sender.sendMessage("Error occurred during reload of AuthMe: aborting");
@ -52,4 +55,16 @@ public class ReloadCommand implements ExecutableCommand {
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;
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.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.permission.AuthGroupHandler;
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.process.Management;
import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import javax.inject.Inject;
import java.util.List;
import static fr.xephi.authme.util.BukkitService.TICKS_PER_SECOND;
/**
* Admin command to unregister a player.
*/
@ -35,74 +23,27 @@ public class UnregisterAdminCommand implements ExecutableCommand {
@Inject
private CommandService commandService;
@Inject
private PlayerCache playerCache;
@Inject
private BukkitService bukkitService;
@Inject
private LimboCache limboCache;
@Inject
private LimboPlayerTaskManager limboPlayerTaskManager;
@Inject
private AuthGroupHandler authGroupHandler;
private Management management;
UnregisterAdminCommand() {
}
@Override
public void executeCommand(final CommandSender sender, List<String> arguments) {
// Get the player name
String playerName = arguments.get(0);
String playerNameLowerCase = playerName.toLowerCase();
// Make sure the user is valid
if (!dataSource.isAuthAvailable(playerNameLowerCase)) {
// Make sure the user exists
if (!dataSource.isAuthAvailable(playerName)) {
commandService.send(sender, MessageKey.UNKNOWN_USER);
return;
}
// Remove the player
if (!dataSource.removeAuth(playerNameLowerCase)) {
commandService.send(sender, MessageKey.ERROR);
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));
}
// Get the player from the server and perform unregister
Player target = bukkitService.getPlayerExact(playerName);
management.performUnregisterByAdmin(sender, playerName, target);
}
}

View File

@ -31,11 +31,13 @@ public class VersionCommand implements ExecutableCommand {
+ " v" + AuthMe.getPluginVersion() + ChatColor.GRAY + " (build: " + AuthMe.getPluginBuildNumber() + ")");
sender.sendMessage(ChatColor.GOLD + "Developers:");
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, "games647", "games647", "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 +
"http://dev.bukkit.org/bukkit-plugins/authme-reloaded/");
sender.sendMessage(ChatColor.GOLD + "License: " + ChatColor.WHITE + "GNU GPL v3.0"

View File

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

View File

@ -1,18 +1,17 @@
package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.mail.SendMailSSL;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.entity.Player;
import javax.inject.Inject;
@ -33,53 +32,39 @@ public class RecoverEmailCommand extends PlayerCommand {
private PlayerCache playerCache;
@Inject
// TODO #655: Remove injected AuthMe instance once Authme#mail is encapsulated
private AuthMe plugin;
private SendMailSSL sendMailSsl;
@Override
public void runCommand(Player player, List<String> arguments) {
final String playerMail = arguments.get(0);
final String playerName = player.getName();
if (plugin.mail == null) {
ConsoleLogger.showError("Mail API is not set");
commandService.send(player, MessageKey.ERROR);
if (!sendMailSsl.hasAllInformation()) {
ConsoleLogger.warning("Mail API is not set");
commandService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
return;
}
if (dataSource.isAuthAvailable(playerName)) {
if (PlayerCache.getInstance().isAuthenticated(playerName)) {
commandService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
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);
if (playerCache.isAuthenticated(playerName)) {
commandService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
return;
}
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.command.CommandService;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.mail.SendMailSSL;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.HashAlgorithm;
@ -27,6 +28,9 @@ public class RegisterCommand extends PlayerCommand {
@Inject
private CommandService commandService;
@Inject
private SendMailSSL sendMailSsl;
@Override
public void runCommand(Player player, List<String> arguments) {
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) {
if (commandService.getProperty(EmailSettings.MAIL_ACCOUNT).isEmpty()) {
player.sendMessage("Cannot register: no email address is set for the server. "
+ "Please contact an administrator");
ConsoleLogger.showError("Cannot register player '" + player.getName() + "': no email is set "
+ "to send emails from. Please add one in your config at " + EmailSettings.MAIL_ACCOUNT.getPath());
if (!sendMailSsl.hasAllInformation()) {
commandService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
ConsoleLogger.warning("Cannot register player '" + player.getName() + "': no email or password is set "
+ "to send emails from. Please adjust your config at " + EmailSettings.MAIL_ACCOUNT.getPath());
return;
}

View File

@ -10,6 +10,9 @@ import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Command for a player to unregister himself.
*/
public class UnregisterCommand extends PlayerCommand {
@Inject
@ -24,15 +27,15 @@ public class UnregisterCommand extends PlayerCommand {
@Override
public void runCommand(Player player, List<String> arguments) {
String playerPass = arguments.get(0);
final String playerNameLowerCase = player.getName().toLowerCase();
final String playerName = player.getName();
// Make sure the player is authenticated
if (!playerCache.isAuthenticated(playerNameLowerCase)) {
if (!playerCache.isAuthenticated(playerName)) {
commandService.send(player, MessageKey.NOT_LOGGED_IN);
return;
}
// 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.PermissionNode;
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 org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
@ -48,9 +48,9 @@ public class HelpProvider implements SettingsDependent {
private String helpHeader;
@Inject
HelpProvider(PermissionsManager permissionsManager, NewSetting settings) {
HelpProvider(PermissionsManager permissionsManager, Settings settings) {
this.permissionsManager = permissionsManager;
loadSettings(settings);
reload(settings);
}
private List<String> printHelp(CommandSender sender, FoundCommandResult result, int options) {
@ -102,7 +102,7 @@ public class HelpProvider implements SettingsDependent {
}
@Override
public void loadSettings(NewSetting settings) {
public void reload(Settings settings) {
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.datasource.DataSource;
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 org.bukkit.command.CommandSender;
@ -20,11 +20,11 @@ import java.io.IOException;
public class CrazyLoginConverter implements Converter {
private final DataSource database;
private final NewSetting settings;
private final Settings settings;
private final File dataFolder;
@Inject
CrazyLoginConverter(@DataFolder File dataFolder, DataSource dataSource, NewSetting settings) {
CrazyLoginConverter(@DataFolder File dataFolder, DataSource dataSource, Settings settings) {
this.dataFolder = dataFolder;
this.database = dataSource;
this.settings = settings;
@ -61,7 +61,7 @@ public class CrazyLoginConverter implements Converter {
}
ConsoleLogger.info("CrazyLogin database has been imported correctly");
} 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);
}
}

View File

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

View File

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

View File

@ -1,28 +1,77 @@
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 fr.xephi.authme.initialization.DataFolder;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
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 {
private final AuthMe plugin;
private final DataSource dataSource;
private final File vAuthPasswordsFile;
@Inject
vAuthConverter(AuthMe plugin) {
this.plugin = plugin;
vAuthConverter(@DataFolder File dataFolder, DataSource dataSource) {
vAuthPasswordsFile = new File(dataFolder.getParent(), makePath("vAuth", "passwords.yml"));
this.dataSource = dataSource;
}
@Override
public void execute(CommandSender sender) {
try {
new vAuthFileReader(plugin).convert();
} catch (Exception e) {
sender.sendMessage(e.getMessage());
ConsoleLogger.showError(e.getMessage());
try (Scanner scanner = new Scanner(vAuthPasswordsFile)) {
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();
}
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;
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.plugin.PluginManager;
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 {
private final AuthMe plugin;
@Inject
xAuthConverter(AuthMe plugin) {
this.plugin = plugin;
@DataFolder
private File dataFolder;
@Inject
private DataSource database;
@Inject
private PluginManager pluginManager;
xAuthConverter() {
}
@Override
public void execute(CommandSender sender) {
try {
Class.forName("de.luricos.bukkit.xAuth.xAuth");
xAuthToFlat converter = new xAuthToFlat(plugin, sender);
converter.convert();
convert(sender);
} catch (ClassNotFoundException ce) {
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 java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
@ -54,7 +55,6 @@ public class CacheDataSource implements DataSource {
return executorService.submit(new Callable<Optional<PlayerAuth>>() {
@Override
public Optional<PlayerAuth> call() {
ConsoleLogger.debug("REFRESH " + key);
return load(key);
}
});
@ -139,13 +139,8 @@ public class CacheDataSource implements DataSource {
}
@Override
public Set<String> autoPurgeDatabase(long until) {
Set<String> cleared = source.autoPurgeDatabase(until);
for (String name : cleared) {
cachedAuths.invalidate(name);
}
return cleared;
public Set<String> getRecordsToPurge(long until) {
return source.getRecordsToPurge(until);
}
@Override
@ -160,14 +155,14 @@ public class CacheDataSource implements DataSource {
@Override
public void close() {
source.close();
cachedAuths.invalidateAll();
executorService.shutdown();
try {
executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
ConsoleLogger.writeStackTrace(e);
ConsoleLogger.logException("Could not close executor service:", e);
}
cachedAuths.invalidateAll();
source.close();
}
@Override
@ -190,8 +185,8 @@ public class CacheDataSource implements DataSource {
}
@Override
public void purgeBanned(final Set<String> banned) {
source.purgeBanned(banned);
public void purgeRecords(final Collection<String> banned) {
source.purgeRecords(banned);
cachedAuths.invalidateAll(banned);
}
@ -235,15 +230,6 @@ public class CacheDataSource implements DataSource {
return result;
}
@Override
public boolean updateIp(String user, String ip) {
boolean result = source.updateIp(user, ip);
if (result) {
cachedAuths.refresh(user);
}
return result;
}
@Override
public List<PlayerAuth> getAllAuths() {
return source.getAllAuths();

View File

@ -1,6 +1,6 @@
package fr.xephi.authme.datasource;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings;
/**
@ -23,7 +23,7 @@ public final class Columns {
public final String ID;
public final String IS_LOGGED;
public Columns(NewSetting settings) {
public Columns(Settings settings) {
NAME = settings.getProperty(DatabaseSettings.MYSQL_COL_NAME);
REAL_NAME = settings.getProperty(DatabaseSettings.MYSQL_COL_REALNAME);
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.security.crypts.HashedPassword;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@ -70,13 +71,19 @@ public interface DataSource extends Reloadable {
boolean updatePassword(String user, HashedPassword password);
/**
* Purge all records in the database whose last login was longer ago than
* the given time.
* Get all records in the database whose last login was before the given time.
*
* @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.
@ -123,13 +130,6 @@ public interface DataSource extends Reloadable {
*/
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.
*
@ -187,15 +187,6 @@ public interface DataSource extends Reloadable {
*/
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.
*

View File

@ -1,12 +1,9 @@
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.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.Settings;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@ -17,6 +14,7 @@ import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -41,27 +39,11 @@ public class FlatFile implements DataSource {
*/
private final File source;
public FlatFile() {
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) {
public FlatFile(File source) throws IOException {
this.source = source;
if (!source.exists() && !source.createNewFile()) {
throw new IOException("Could not create file '" + source.getPath() + "'");
}
}
@Override
@ -82,7 +64,7 @@ public class FlatFile implements DataSource {
}
}
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.warning(ex.getMessage());
return false;
} finally {
silentClose(br);
@ -109,7 +91,7 @@ public class FlatFile implements DataSource {
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");
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.warning(ex.getMessage());
return false;
} finally {
silentClose(bw);
@ -145,7 +127,7 @@ public class FlatFile implements DataSource {
}
}
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.warning(ex.getMessage());
return false;
} finally {
silentClose(br);
@ -179,7 +161,7 @@ public class FlatFile implements DataSource {
}
}
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.warning(ex.getMessage());
return false;
} finally {
silentClose(br);
@ -216,7 +198,7 @@ public class FlatFile implements DataSource {
}
}
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.warning(ex.getMessage());
return false;
} finally {
silentClose(br);
@ -229,11 +211,10 @@ public class FlatFile implements DataSource {
}
@Override
public Set<String> autoPurgeDatabase(long until) {
public Set<String> getRecordsToPurge(long until) {
BufferedReader br = null;
BufferedWriter bw = null;
ArrayList<String> lines = new ArrayList<>();
Set<String> cleared = new HashSet<>();
Set<String> list = new HashSet<>();
try {
br = new BufferedReader(new FileReader(source));
String line;
@ -241,25 +222,24 @@ public class FlatFile implements DataSource {
String[] args = line.split(":");
if (args.length >= 4) {
if (Long.parseLong(args[3]) >= until) {
lines.add(line);
list.add(args[0]);
continue;
}
}
cleared.add(args[0]);
}
bw = new BufferedWriter(new FileWriter(source));
for (String l : lines) {
bw.write(l + "\n");
}
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
return cleared;
ConsoleLogger.warning(ex.getMessage());
return list;
} finally {
silentClose(br);
silentClose(bw);
}
return cleared;
return list;
}
@Override
public void purgeRecords(Collection<String> toPurge) {
throw new UnsupportedOperationException("Flat file no longer supported");
}
@Override
@ -284,7 +264,7 @@ public class FlatFile implements DataSource {
bw.write(l + "\n");
}
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.warning(ex.getMessage());
return false;
} finally {
silentClose(br);
@ -306,7 +286,7 @@ public class FlatFile implements DataSource {
}
}
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.warning(ex.getMessage());
return null;
} finally {
silentClose(br);
@ -339,10 +319,10 @@ public class FlatFile implements DataSource {
}
}
} catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.warning(ex.getMessage());
return false;
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.warning(ex.getMessage());
return false;
} finally {
if (br != null) {
@ -374,10 +354,10 @@ public class FlatFile implements DataSource {
}
return countIp;
} catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.warning(ex.getMessage());
return new ArrayList<>();
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.warning(ex.getMessage());
return new ArrayList<>();
} finally {
if (br != null) {
@ -404,7 +384,7 @@ public class FlatFile implements DataSource {
}
return countEmail;
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.warning(ex.getMessage());
} finally {
if (br != null) {
try {
@ -416,37 +396,6 @@ public class FlatFile implements DataSource {
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
public DataSourceType getType() {
return DataSourceType.FILE;
@ -479,7 +428,7 @@ public class FlatFile implements DataSource {
result++;
}
} catch (Exception ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.warning(ex.getMessage());
return result;
} finally {
silentClose(br);
@ -492,11 +441,6 @@ public class FlatFile implements DataSource {
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
public List<PlayerAuth> getAllAuths() {
BufferedReader br = null;

View File

@ -3,13 +3,11 @@ package fr.xephi.authme.datasource;
import com.google.common.annotations.VisibleForTesting;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.security.crypts.XFBCRYPT;
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.HooksSettings;
@ -25,72 +23,65 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MySQL implements DataSource {
private final String host;
private final String port;
private final String username;
private final String password;
private final String database;
private final String tableName;
private final List<String> columnOthers;
private final Columns col;
private final HashAlgorithm hashAlgorithm;
private String host;
private String port;
private String username;
private String password;
private String database;
private String tableName;
private List<String> columnOthers;
private Columns col;
private HashAlgorithm hashAlgorithm;
private HikariDataSource ds;
private final String phpBbPrefix;
private final int phpBbGroup;
private final String wordpressPrefix;
private String phpBbPrefix;
private int phpBbGroup;
private String wordpressPrefix;
public MySQL(NewSetting settings) throws ClassNotFoundException, SQLException, PoolInitializationException {
this.host = settings.getProperty(DatabaseSettings.MYSQL_HOST);
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);
public MySQL(Settings settings) throws ClassNotFoundException, SQLException, PoolInitializationException {
setParameters(settings);
// Set the connection arguments (and check if connection is ok)
try {
this.setConnectionArguments();
} catch (RuntimeException e) {
if (e instanceof IllegalArgumentException) {
ConsoleLogger.showError("Invalid database arguments! Please check your configuration!");
ConsoleLogger.showError("If this error persists, please report it to the developer!");
throw new IllegalArgumentException(e);
ConsoleLogger.warning("Invalid database arguments! Please check your configuration!");
ConsoleLogger.warning("If this error persists, please report it to the developer!");
}
if (e instanceof PoolInitializationException) {
ConsoleLogger.showError("Can't initialize database connection! Please check your configuration!");
ConsoleLogger.showError("If this error persists, please report it to the developer!");
throw new PoolInitializationException(e);
ConsoleLogger.warning("Can't initialize database connection! Please check your configuration!");
ConsoleLogger.warning("If this error persists, please report it to the developer!");
}
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;
}
// Initialize the database
try {
this.setupConnection();
checkTablesAndColumns();
} catch (SQLException e) {
this.close();
ConsoleLogger.showError("Can't initialize the MySQL database... Please check your database settings in the config.yml file! SHUTDOWN...");
ConsoleLogger.showError("If this error persists, please report it to the developer!");
close();
ConsoleLogger.logException("Can't initialize the MySQL database:", e);
ConsoleLogger.warning("Please check your database settings in the config.yml file!");
throw e;
}
}
@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.port = settings.getProperty(DatabaseSettings.MYSQL_PORT);
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.phpBbGroup = settings.getProperty(HooksSettings.PHPBB_ACTIVATED_GROUP_ID);
this.wordpressPrefix = settings.getProperty(HooksSettings.WORDPRESS_TABLE_PREFIX);
ds = hikariDataSource;
}
private void setConnectionArguments() throws RuntimeException {
@ -147,118 +137,83 @@ public class MySQL implements DataSource {
return ds.getConnection();
}
private void setupConnection() throws SQLException {
try (Connection con = getConnection()) {
Statement st = con.createStatement();
DatabaseMetaData md = con.getMetaData();
// Create table if not exists.
private void checkTablesAndColumns() throws SQLException {
try (Connection con = getConnection(); Statement st = con.createStatement()) {
// Create table with ID column if it doesn't exist
String sql = "CREATE TABLE IF NOT EXISTS " + tableName + " ("
+ col.ID + " INTEGER AUTO_INCREMENT,"
+ col.NAME + " VARCHAR(255) NOT NULL UNIQUE,"
+ col.REAL_NAME + " VARCHAR(255) NOT NULL,"
+ 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 + ")"
+ ");";
+ col.ID + " MEDIUMINT(8) UNSIGNED AUTO_INCREMENT,"
+ "PRIMARY KEY (" + col.ID + ")"
+ ") CHARACTER SET = utf8;";
st.executeUpdate(sql);
ResultSet rs = md.getColumns(null, null, tableName, col.NAME);
if (!rs.next()) {
DatabaseMetaData md = con.getMetaData();
if (isColumnMissing(md, col.NAME)) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.NAME + " VARCHAR(255) NOT NULL UNIQUE AFTER " + col.ID + ";");
}
rs.close();
rs = md.getColumns(null, null, tableName, col.REAL_NAME);
if (!rs.next()) {
if (isColumnMissing(md, col.REAL_NAME)) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.REAL_NAME + " VARCHAR(255) NOT NULL AFTER " + col.NAME + ";");
}
rs.close();
rs = md.getColumns(null, null, tableName, col.PASSWORD);
if (!rs.next()) {
if (isColumnMissing(md, col.PASSWORD)) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.PASSWORD + " VARCHAR(255) 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();
+ " ADD COLUMN " + col.PASSWORD + " VARCHAR(255) CHARACTER SET ascii COLLATE ascii_bin NOT NULL;");
}
rs = md.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.SALT + " VARCHAR(255);");
}
if (isColumnMissing(md, col.IP)) {
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 (!rs.next()) {
if (isColumnMissing(md, col.LAST_LOGIN)) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.LAST_LOGIN + " BIGINT NOT NULL DEFAULT 0;");
} else {
migrateLastLoginColumnToBigInt(con, rs);
migrateLastLoginColumn(con, md);
}
rs.close();
rs = md.getColumns(null, null, tableName, col.LASTLOC_X);
if (!rs.next()) {
if (isColumnMissing(md, col.LASTLOC_X)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
+ 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_Z + " DOUBLE NOT NULL DEFAULT '0.0' AFTER " + col.LASTLOC_Y);
}
rs.close();
rs = md.getColumns(null, null, tableName, col.LASTLOC_X);
if (rs.next()) {
} else {
st.executeUpdate("ALTER TABLE " + tableName + " MODIFY "
+ col.LASTLOC_X + " 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';");
}
rs.close();
rs = md.getColumns(null, null, tableName, col.LASTLOC_WORLD);
if (!rs.next()) {
if (isColumnMissing(md, col.LASTLOC_WORLD)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
+ col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT 'world' AFTER " + col.LASTLOC_Z);
}
rs.close();
rs = md.getColumns(null, null, tableName, col.EMAIL);
if (!rs.next()) {
if (isColumnMissing(md, col.EMAIL)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
+ col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com' AFTER " + col.LASTLOC_WORLD);
}
rs.close();
rs = md.getColumns(null, null, tableName, col.IS_LOGGED);
if (!rs.next()) {
if (isColumnMissing(md, col.IS_LOGGED)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
+ col.IS_LOGGED + " SMALLINT NOT NULL DEFAULT '0' AFTER " + col.EMAIL);
}
rs.close();
st.close();
}
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
public boolean isAuthAvailable(String user) {
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
@ -613,21 +568,18 @@ public class MySQL implements DataSource {
}
@Override
public Set<String> autoPurgeDatabase(long until) {
public Set<String> getRecordsToPurge(long until) {
Set<String> list = new HashSet<>();
String select = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;";
String delete = "DELETE FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;";
try (Connection con = getConnection();
PreparedStatement selectPst = con.prepareStatement(select);
PreparedStatement deletePst = con.prepareStatement(delete)) {
PreparedStatement selectPst = con.prepareStatement(select)) {
selectPst.setLong(1, until);
try (ResultSet rs = selectPst.executeQuery()) {
while (rs.next()) {
list.add(rs.getString(col.NAME));
}
}
deletePst.setLong(1, until);
deletePst.executeUpdate();
} catch (SQLException ex) {
logSqlException(ex);
}
@ -742,11 +694,11 @@ public class MySQL implements DataSource {
}
@Override
public void purgeBanned(Set<String> banned) {
public void purgeRecords(Collection<String> toPurge) {
String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
for (String name : banned) {
pst.setString(1, name);
for (String name : toPurge) {
pst.setString(1, name.toLowerCase());
pst.executeUpdate();
}
} catch (SQLException ex) {
@ -839,20 +791,6 @@ public class MySQL implements DataSource {
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
public List<PlayerAuth> getAllAuths() {
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 rs ResultSet containing meta data for the lastlogin column
* @param con connection to the database
* @param metaData lastlogin column meta data
* @throws SQLException
*/
private void migrateLastLoginColumnToBigInt(Connection con, ResultSet rs) throws SQLException {
final int columnType = rs.getInt("DATA_TYPE");
if (columnType == Types.TIMESTAMP) {
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)",
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)");
private void migrateLastLoginColumn(Connection con, DatabaseMetaData metaData) throws SQLException {
final int columnType;
try (ResultSet rs = metaData.getColumns(null, null, tableName, col.LAST_LOGIN)) {
if (!rs.next()) {
ConsoleLogger.warning("Could not get LAST_LOGIN meta data. This should never happen!");
return;
}
columnType = rs.getInt("DATA_TYPE");
}
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) {

View File

@ -1,26 +1,26 @@
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.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
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 {
@ -38,7 +38,7 @@ public class SQLite implements DataSource {
* @throws ClassNotFoundException if no driver could be found for the datasource
* @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.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
this.col = new Columns(settings);
@ -53,7 +53,7 @@ public class SQLite implements DataSource {
}
@VisibleForTesting
SQLite(NewSetting settings, Connection connection) {
SQLite(Settings settings, Connection connection) {
this.database = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
this.col = new Columns(settings);
@ -70,67 +70,73 @@ public class SQLite implements DataSource {
this.con = DriverManager.getConnection("jdbc:sqlite:plugins/AuthMe/" + database + ".db");
}
private void setup() throws SQLException {
Statement st = null;
ResultSet rs = null;
try {
st = con.createStatement();
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 + "));");
rs = con.getMetaData().getColumns(null, null, tableName, col.PASSWORD);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.PASSWORD + " VARCHAR(255) NOT NULL;");
@VisibleForTesting
protected void setup() throws SQLException {
try (Statement st = con.createStatement()) {
// Note: cannot add unique fields later on in SQLite, so we add it on initialization
st.executeUpdate("CREATE TABLE IF NOT EXISTS " + tableName + " ("
+ col.ID + " INTEGER AUTO_INCREMENT, "
+ col.NAME + " VARCHAR(255) NOT NULL UNIQUE, "
+ "CONSTRAINT table_const_prim PRIMARY KEY (" + col.ID + "));");
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()) {
rs = con.getMetaData().getColumns(null, null, tableName, col.SALT);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.SALT + " VARCHAR(255);");
}
rs.close();
if (isColumnMissing(md, col.PASSWORD)) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.PASSWORD + " VARCHAR(255) NOT NULL DEFAULT '';");
}
rs = con.getMetaData().getColumns(null, null, tableName, col.IP);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.IP + " VARCHAR(40) NOT NULL;");
if (!col.SALT.isEmpty() && isColumnMissing(md, col.SALT)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.SALT + " VARCHAR(255);");
}
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.LAST_LOGIN);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LAST_LOGIN + " TIMESTAMP DEFAULT current_timestamp;");
if (isColumnMissing(md, col.IP)) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.IP + " VARCHAR(40) NOT NULL DEFAULT '';");
}
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.LASTLOC_X);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_X + " 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';");
if (isColumnMissing(md, col.LAST_LOGIN)) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.LAST_LOGIN + " TIMESTAMP;");
}
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.LASTLOC_WORLD);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT 'world';");
if (isColumnMissing(md, col.LASTLOC_X)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_X
+ " 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 (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com';");
if (isColumnMissing(md, col.LASTLOC_WORLD)) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT 'world';");
}
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.IS_LOGGED);
if (!rs.next()) {
if (isColumnMissing(md, col.EMAIL)) {
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';");
}
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");
}
private boolean isColumnMissing(DatabaseMetaData metaData, String columnName) throws SQLException {
try (ResultSet rs = metaData.getColumns(null, null, tableName, columnName)) {
return !rs.next();
}
}
@Override
public void reload() {
close(con);
@ -147,12 +153,12 @@ public class SQLite implements DataSource {
PreparedStatement pst = null;
ResultSet rs = null;
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);
rs = pst.executeQuery();
return rs.next();
} catch (SQLException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.warning(ex.getMessage());
return false;
} finally {
close(rs);
@ -207,7 +213,7 @@ public class SQLite implements DataSource {
HashedPassword password = auth.getPassword();
if (col.SALT.isEmpty()) {
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!");
}
pst = con.prepareStatement("INSERT INTO " + tableName + "(" + col.NAME + "," + col.PASSWORD +
@ -293,20 +299,17 @@ public class SQLite implements DataSource {
}
@Override
public Set<String> autoPurgeDatabase(long until) {
public Set<String> getRecordsToPurge(long until) {
Set<String> list = new HashSet<>();
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);
PreparedStatement deletePst = con.prepareStatement(delete)) {
try (PreparedStatement selectPst = con.prepareStatement(select)) {
selectPst.setLong(1, until);
try (ResultSet rs = selectPst.executeQuery()) {
while (rs.next()) {
list.add(rs.getString(col.NAME));
}
}
deletePst.setLong(1, until);
deletePst.executeUpdate();
} catch (SQLException ex) {
logSqlException(ex);
}
@ -314,6 +317,19 @@ public class SQLite implements DataSource {
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
public boolean removeAuth(String user) {
PreparedStatement pst = null;
@ -443,19 +459,6 @@ public class SQLite implements DataSource {
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
public DataSourceType getType() {
return DataSourceType.SQLITE;
@ -553,20 +556,6 @@ public class SQLite implements DataSource {
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
public List<PlayerAuth> getAllAuths() {
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.ByteStreams;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.BukkitService;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
import javax.inject.Inject;
public class BungeeCordMessage implements PluginMessageListener {
@Inject
@ -28,9 +24,6 @@ public class BungeeCordMessage implements PluginMessageListener {
@Inject
private PlayerCache playerCache;
@Inject
private AuthMe plugin;
BungeeCordMessage() { }
@ -56,26 +49,13 @@ public class BungeeCordMessage implements PluginMessageListener {
if ("login".equals(act)) {
playerCache.updatePlayer(auth);
dataSource.setLogged(name);
//START 03062016 sgdc3: should fix #731 but we need to recode this mess
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!");
}
ConsoleLogger.fine("Player " + auth.getNickname() + " has logged in from one of your server!");
} else if ("logout".equals(act)) {
playerCache.removePlayer(name);
dataSource.setUnlogged(name);
if (!plugin.getSettings().getProperty(SecuritySettings.REMOVE_SPAM_FROM_CONSOLE)) {
ConsoleLogger.info("Player " + auth.getNickname() + " has logged out from one of your server!");
}
ConsoleLogger.fine("Player " + auth.getNickname() + " has logged out from one of your server!");
} else if ("register".equals(act)) {
if (!plugin.getSettings().getProperty(SecuritySettings.REMOVE_SPAM_FROM_CONSOLE)) {
ConsoleLogger.info("Player " + auth.getNickname() + " has registered out from one of your server!");
}
ConsoleLogger.fine("Player " + auth.getNickname() + " has registered out from one of your server!");
} else if ("changepassword".equals(act)) {
final String password = args[2];
final String salt = args.length >= 4 ? args[3] : null;

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.hooks;
import ch.jalu.injector.annotations.NoFieldScan;
import com.earth2me.essentials.Essentials;
import com.onarandombox.MultiverseCore.MultiverseCore;
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.
*/
@NoFieldScan
public class PluginHooks {
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.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.PluginSettings;
import org.mcstats.Metrics;
@ -10,12 +10,12 @@ import org.mcstats.Metrics.Graph;
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 {
final Metrics metrics = new Metrics(plugin);
@ -41,7 +41,7 @@ public class MetricsStarter {
metrics.start();
} catch (IOException e) {
// 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;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.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 {
/**
* Loads the needed settings.
* Performs a reload with the provided 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;
public class AuthMeBlockListener implements Listener {
public class BlockListener implements Listener {
@Inject
private ListenerService listenerService;

View File

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

View File

@ -6,9 +6,9 @@ import fr.xephi.authme.util.StringUtils;
/**
* Exception thrown when a verification has failed.
*/
@SuppressWarnings("serial")
public class FailedVerificationException extends Exception {
private static final long serialVersionUID = 3903242223297960699L;
private final MessageKey reason;
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.hooks.PluginHooks;
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.RestrictionSettings;
import fr.xephi.authme.util.ValidationService;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityEvent;
import org.bukkit.event.player.PlayerEvent;
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.
@ -24,16 +22,18 @@ class ListenerService implements SettingsDependent {
private final DataSource dataSource;
private final PluginHooks pluginHooks;
private final PlayerCache playerCache;
private final ValidationService validationService;
private boolean isRegistrationForced;
private Set<String> unrestrictedNames;
@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.pluginHooks = pluginHooks;
this.playerCache = playerCache;
loadSettings(settings);
this.validationService = validationService;
reload(settings);
}
/**
@ -44,10 +44,19 @@ class ListenerService implements SettingsDependent {
*/
public boolean shouldCancelEvent(EntityEvent event) {
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)) {
return false;
}
Player player = (Player) entity;
return shouldCancelEvent(player);
}
@ -74,10 +83,8 @@ class ListenerService implements SettingsDependent {
}
@Override
public void loadSettings(NewSetting settings) {
public void reload(Settings settings) {
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
*/
private boolean checkAuth(String name) {
if (isUnrestricted(name) || playerCache.isAuthenticated(name)) {
if (validationService.isUnrestricted(name) || playerCache.isAuthenticated(name)) {
return true;
}
if (!isRegistrationForced && !dataSource.isAuthAvailable(name)) {
@ -96,14 +103,4 @@ class ListenerService implements SettingsDependent {
}
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.permission.PermissionsManager;
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.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.StringUtils;
import fr.xephi.authme.util.Utils;
import fr.xephi.authme.util.ValidationService;
import org.bukkit.Server;
import org.bukkit.entity.Player;
@ -31,7 +32,7 @@ import java.util.regex.Pattern;
class OnJoinVerifier implements Reloadable {
@Inject
private NewSetting settings;
private Settings settings;
@Inject
private DataSource dataSource;
@Inject
@ -56,13 +57,7 @@ class OnJoinVerifier implements Reloadable {
@Override
public void reload() {
String nickRegEx = settings.getProperty(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS);
try {
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");
}
nicknamePattern = Utils.safePatternCompile(nickRegEx);
}
/**
@ -73,7 +68,7 @@ class OnJoinVerifier implements Reloadable {
*/
public void checkAntibot(String playerName, boolean isAuthAvailable) throws FailedVerificationException {
if (antiBot.getAntiBotStatus() == AntiBot.AntiBotStatus.ACTIVE && !isAuthAvailable) {
antiBot.antibotKicked.addIfAbsent(playerName);
antiBot.addPlayerKick(playerName);
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 event the login event of the player
* @param playerIp the ip address of the player
*/
public void checkPlayerCountry(boolean isAuthAvailable,
PlayerLoginEvent event) throws FailedVerificationException {
if (!isAuthAvailable && settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)) {
String playerIp = event.getAddress().getHostAddress();
String playerIp) throws FailedVerificationException {
if ((!isAuthAvailable || settings.getProperty(ProtectionSettings.ENABLE_PROTECTION_REGISTERED))
&& settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)) {
if (!validationService.isCountryAdmitted(playerIp)) {
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.Messages;
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.properties.HooksSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
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.entity.Player;
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.InventoryOpenEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerBedEnterEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
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.
*/
public class AuthMePlayerListener implements Listener {
public class PlayerListener implements Listener {
public static final ConcurrentHashMap<String, String> joinMessage = new ConcurrentHashMap<>();
@Inject
private NewSetting settings;
private Settings settings;
@Inject
private Messages m;
@Inject
@ -73,6 +73,10 @@ public class AuthMePlayerListener implements Listener {
private OnJoinVerifier onJoinVerifier;
@Inject
private ListenerService listenerService;
@Inject
private TeleportationService teleportationService;
@Inject
private ValidationService validationService;
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
@ -141,10 +145,6 @@ public class AuthMePlayerListener implements Listener {
if (!settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)) {
// "cancel" the event
event.setTo(event.getFrom());
if (settings.getProperty(RestrictionSettings.REMOVE_SPEED)) {
player.setFlySpeed(0.0f);
player.setWalkSpeed(0.0f);
}
return;
}
@ -189,47 +189,24 @@ public class AuthMePlayerListener implements Listener {
}
}
@EventHandler(priority = EventPriority.LOW)
@EventHandler(priority = EventPriority.NORMAL)
public void onPlayerJoin(PlayerJoinEvent event) {
final Player player = event.getPlayer();
if (player != null) {
// Schedule login task so works after the prelogin
// (Fix found by Koolaid5000)
bukkitService.runTask(new Runnable() {
@Override
public void run() {
management.performJoin(player);
}
});
}
teleportationService.teleportNewPlayerToFirstSpawn(player);
management.performJoin(player);
}
// Note ljacqu 20160528: 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
@EventHandler(priority = EventPriority.HIGHEST)
public void onPreLogin(AsyncPlayerPreLoginEvent event) {
final String name = event.getName().toLowerCase();
final boolean isAuthAvailable = dataSource.isAuthAvailable(event.getName());
// Note #831: AsyncPlayerPreLoginEvent is not fired by all servers in offline mode
// e.g. CraftBukkit does not fire it. So we need to run crucial things with PlayerLoginEvent.
// Single session feature can be implemented for Spigot and CraftBukkit by canceling a kick
// event caused by "logged in from another location". The nicer way, but only for Spigot, would be
// to check in the AsyncPlayerPreLoginEvent. To support all servers, we use the less nice way.
try {
// 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)
@EventHandler(priority = EventPriority.LOW)
public void onPlayerLogin(PlayerLoginEvent event) {
final Player player = event.getPlayer();
if (Utils.isUnrestricted(player)) {
final String name = player.getName();
if (validationService.isUnrestricted(name)) {
return;
} else if (onJoinVerifier.refusePlayerForFullServer(event)) {
return;
@ -237,16 +214,20 @@ public class AuthMePlayerListener implements Listener {
return;
}
final String name = player.getName().toLowerCase();
final PlayerAuth auth = dataSource.getAuth(player.getName());
final boolean isAuthAvailable = (auth != null);
try {
onJoinVerifier.checkAntibot(name, isAuthAvailable);
onJoinVerifier.checkKickNonRegistered(isAuthAvailable);
// Fast stuff
onJoinVerifier.checkSingleSession(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.checkPlayerCountry(isAuthAvailable, event);
onJoinVerifier.checkPlayerCountry(isAuthAvailable, event.getAddress().getHostAddress());
} catch (FailedVerificationException e) {
event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs()));
event.setResult(PlayerLoginEvent.Result.KICK_OTHER);
@ -254,6 +235,7 @@ public class AuthMePlayerListener implements Listener {
}
antiBot.handlePlayerJoin(player);
teleportationService.teleportOnJoin(player);
}
@EventHandler(priority = EventPriority.HIGHEST)
@ -262,21 +244,32 @@ public class AuthMePlayerListener implements Listener {
if (settings.getProperty(RegistrationSettings.REMOVE_LEAVE_MESSAGE)) {
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;
}
management.performQuit(player, false);
management.performQuit(player);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
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())) {
management.performQuit(player, true);
final Player player = event.getPlayer();
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.
*/
public class AuthMePlayerListener16 implements Listener {
public class PlayerListener16 implements Listener {
@Inject
private ListenerService listenerService;

View File

@ -10,7 +10,7 @@ import javax.inject.Inject;
/**
* Listener of player events for events introduced in Minecraft 1.8.
*/
public class AuthMePlayerListener18 implements Listener {
public class PlayerListener18 implements Listener {
@Inject
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;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.hooks.PluginHooks;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages;
import fr.xephi.authme.listener.protocollib.ProtocolLibService;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.NewSetting;
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.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.event.server.PluginEnableEvent;
import org.bukkit.event.server.ServerListPingEvent;
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
private PluginHooks pluginHooks;
@Inject
private SpawnLoader spawnLoader;
@Inject
private ValidationService validationService;
private ProtocolLibService protocolLibService;
@Inject
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)
public void onPluginDisable(PluginDisableEvent event) {
// Make sure the plugin instance isn't null
@ -72,13 +49,9 @@ public class AuthMeServerListener implements Listener {
} else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) {
spawnLoader.unloadEssentialsSpawn();
ConsoleLogger.info("EssentialsSpawn has been disabled: unhooking");
}
if (pluginName.equalsIgnoreCase("ProtocolLib")) {
plugin.inventoryProtector = null;
plugin.tablistHider = null;
plugin.tabComplete = null;
ConsoleLogger.showError("ProtocolLib has been disabled, unhook packet inventory protection!");
} else if ("ProtocolLib".equalsIgnoreCase(pluginName)) {
protocolLibService.disable();
ConsoleLogger.warning("ProtocolLib has been disabled, unhooking packet adapters!");
}
}
@ -102,10 +75,8 @@ public class AuthMeServerListener implements Listener {
pluginHooks.tryHookToCombatPlus();
} else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) {
spawnLoader.loadEssentialsSpawn();
}
if (pluginName.equalsIgnoreCase("ProtocolLib")) {
plugin.checkProtocolLib();
} else if ("ProtocolLib".equalsIgnoreCase(pluginName)) {
protocolLibService.setup();
}
}
}

View File

@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* 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.ProtocolLibrary;
@ -22,24 +22,16 @@ import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import fr.xephi.authme.AuthMe;
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.entity.Player;
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;
public class AuthMeInventoryPacketAdapter extends PacketAdapter {
class InventoryPacketAdapter extends PacketAdapter {
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)
@ -48,12 +40,8 @@ public class AuthMeInventoryPacketAdapter extends PacketAdapter {
private static final int ARMOR_SIZE = 4;
private static final int MAIN_SIZE = 27;
private static final int HOTBAR_SIZE = 9;
private static final int OFF_HAND_POSITION = 45;
private final boolean offHandSupported = MethodUtils
.getAccessibleMethod(PlayerInventory.class, "getItemInOffHand", new Class[]{}) != null;
public AuthMeInventoryPacketAdapter(AuthMe plugin) {
public InventoryPacketAdapter(AuthMe plugin) {
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();
byte windowId = packet.getIntegers().read(0).byteValue();
if (windowId == PLAYER_INVENTORY && Settings.protectInventoryBeforeLogInEnabled
&& !PlayerCache.getInstance().isAuthenticated(player.getName())) {
if (windowId == PLAYER_INVENTORY && !PlayerCache.getInstance().isAuthenticated(player.getName())) {
packetEvent.setCancelled(true);
}
}
@ -77,52 +64,6 @@ public class AuthMeInventoryPacketAdapter extends PacketAdapter {
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) {
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
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.ProtocolLibrary;
@ -6,17 +6,13 @@ import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.reflect.FieldAccessException;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerCache;
import javax.inject.Inject;
class TabCompletePacketAdapter extends PacketAdapter {
public class AuthMeTabCompletePacketAdapter extends PacketAdapter {
@Inject
public AuthMeTabCompletePacketAdapter(AuthMe plugin) {
public TabCompletePacketAdapter(AuthMe plugin) {
super(plugin, ListenerPriority.NORMAL, PacketType.Play.Client.TAB_COMPLETE);
}
@ -28,7 +24,7 @@ public class AuthMeTabCompletePacketAdapter extends PacketAdapter {
event.setCancelled(true);
}
} 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.ConsoleLogger;
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.util.BukkitService;
import fr.xephi.authme.util.StringUtils;
import org.apache.commons.mail.EmailConstants;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.bukkit.Bukkit;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.imageio.ImageIO;
import javax.inject.Inject;
import javax.mail.Session;
import java.io.File;
import java.io.IOException;
import java.security.Security;
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
*/
public class SendMailSSL {
private final AuthMe plugin;
private final NewSetting settings;
@Inject
private AuthMe plugin;
@Inject
private Settings settings;
@Inject
private BukkitService bukkitService;
public SendMailSSL(AuthMe plugin, NewSetting settings) {
this.plugin = plugin;
this.settings = settings;
SendMailSSL() {
}
public void main(final PlayerAuth auth, final String newPass) {
final String mailText = replaceMailTags(settings.getEmailMessage(), plugin, auth, newPass);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
/**
* Returns whether all necessary settings are set for sending mails.
*
* @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
public void run() {
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
HtmlEmail email;
try {
email = initializeMail(auth, settings);
email = initializeMail(auth.getEmail());
} catch (EmailException e) {
ConsoleLogger.logException("Failed to create email with the given settings:", e);
return;
@ -54,7 +81,7 @@ public class SendMailSSL {
File file = null;
if (settings.getProperty(EmailSettings.PASSWORD_AS_IMAGE)) {
try {
file = generateImage(auth, plugin, newPass);
file = generateImage(auth.getNickname(), plugin, newPass);
content = embedImageIntoEmailContent(file, email, content);
} catch (IOException | EmailException e) {
ConsoleLogger.logException(
@ -67,13 +94,12 @@ public class SendMailSSL {
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);
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);
return file;
}
@ -85,8 +111,7 @@ public class SendMailSSL {
return content.replace("<image />", "<img src=\"cid:" + tag + "\">");
}
private static HtmlEmail initializeMail(PlayerAuth auth, NewSetting settings)
throws EmailException {
private HtmlEmail initializeMail(String emailAddress) throws EmailException {
String senderMail = settings.getProperty(EmailSettings.MAIL_ACCOUNT);
String senderName = StringUtils.isEmpty(settings.getProperty(EmailSettings.MAIL_SENDER_NAME))
? senderMail
@ -98,12 +123,12 @@ public class SendMailSSL {
email.setCharset(EmailConstants.UTF_8);
email.setSmtpPort(port);
email.setHostName(settings.getProperty(EmailSettings.SMTP_HOST));
email.addTo(auth.getEmail());
email.addTo(emailAddress);
email.setFrom(senderMail, senderName);
email.setSubject(settings.getProperty(EmailSettings.RECOVERY_MAIL_SUBJECT));
email.setAuthentication(senderMail, mailPassword);
setPropertiesForPort(email, port, settings);
setPropertiesForPort(email, port);
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
.replace("<playername />", auth.getNickname())
.replace("<servername />", plugin.getServer().getServerName())
.replace("<generatedpass />", newPass);
}
private static void setPropertiesForPort(HtmlEmail email, int port, NewSetting settings)
throws EmailException {
private void setPropertiesForPort(HtmlEmail email, int port) throws EmailException {
switch (port) {
case 587:
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.Marker;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.message.Message;
/**
@ -12,7 +12,8 @@ import org.apache.logging.log4j.message.Message;
*
* @author Xephi59
*/
public class Log4JFilter implements Filter {
@SuppressWarnings("serial")
public class Log4JFilter extends AbstractFilter {
/**
* Constructor.
@ -50,42 +51,30 @@ public class Log4JFilter implements Filter {
}
@Override
public Result filter(LogEvent record) {
if (record == null) {
return Result.NEUTRAL;
public Result filter(LogEvent event) {
Message candidate = null;
if(event != null) {
candidate = event.getMessage();
}
return validateMessage(record.getMessage());
return validateMessage(candidate);
}
@Override
public Result filter(Logger arg0, Level arg1, Marker arg2, String message, Object... arg4) {
if (message == null) {
return Result.NEUTRAL;
public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
return validateMessage(msg);
}
@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_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[] tags;

View File

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

View File

@ -2,10 +2,12 @@ package fr.xephi.authme.permission;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.limbo.LimboCache;
import fr.xephi.authme.cache.limbo.LimboPlayer;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.cache.limbo.PlayerData;
import fr.xephi.authme.initialization.Reloadable;
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.SecuritySettings;
import org.bukkit.entity.Player;
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.
*/
public class AuthGroupHandler {
public class AuthGroupHandler implements Reloadable {
@Inject
private PermissionsManager permissionsManager;
@Inject
private NewSetting settings;
private Settings settings;
@Inject
private LimboCache limboCache;
private String unloggedInGroup;
private String unregisteredGroup;
private String registeredGroup;
AuthGroupHandler() { }
/**
@ -44,41 +50,74 @@ public class AuthGroupHandler {
// Make sure group support is available
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;
}
switch (group) {
case UNREGISTERED:
// Remove the other group type groups, set the current group
permissionsManager.removeGroups(player, Arrays.asList(Settings.getRegisteredGroup, Settings.getUnloggedinGroup));
return permissionsManager.addGroup(player, Settings.unRegisteredGroup);
permissionsManager.removeGroups(player, Arrays.asList(registeredGroup, unloggedInGroup));
return permissionsManager.addGroup(player, unregisteredGroup);
case REGISTERED:
// Remove the other group type groups, set the current group
permissionsManager.removeGroups(player, Arrays.asList(Settings.unRegisteredGroup, Settings.getUnloggedinGroup));
return permissionsManager.addGroup(player, Settings.getRegisteredGroup);
permissionsManager.removeGroups(player, Arrays.asList(unregisteredGroup, unloggedInGroup));
return permissionsManager.addGroup(player, registeredGroup);
case NOT_LOGGED_IN:
// Remove the other group type groups, set the current group
permissionsManager.removeGroups(player, Arrays.asList(Settings.unRegisteredGroup, Settings.getRegisteredGroup));
return permissionsManager.addGroup(player, Settings.getUnloggedinGroup);
permissionsManager.removeGroups(player, Arrays.asList(unregisteredGroup, registeredGroup));
return permissionsManager.addGroup(player, unloggedInGroup);
case LOGGED_IN:
// Get the limbo player data
LimboPlayer limbo = limboCache.getLimboPlayer(player.getName().toLowerCase());
if (limbo == null)
// Get the player data
PlayerData data = limboCache.getPlayerData(player.getName().toLowerCase());
if (data == null) {
return false;
}
// Get the players group
String realGroup = limbo.getGroup();
String realGroup = data.getGroup();
// 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);
default:
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;
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.
@ -10,7 +10,7 @@ public enum DefaultPermission {
/** No one has permission. */
NOT_ALLOWED("No permission") {
@Override
public boolean evaluate(CommandSender sender) {
public boolean evaluate(ServerOperator sender) {
return false;
}
},
@ -18,15 +18,15 @@ public enum DefaultPermission {
/** Only players with OP status have permission. */
OP_ONLY("OP's only") {
@Override
public boolean evaluate(CommandSender sender) {
return sender.isOp();
public boolean evaluate(ServerOperator sender) {
return sender != null && sender.isOp();
}
},
/** Everyone is granted permission. */
ALLOWED("Everyone allowed") {
@Override
public boolean evaluate(CommandSender sender) {
public boolean evaluate(ServerOperator sender) {
return true;
}
};
@ -48,7 +48,7 @@ public enum DefaultPermission {
* @param sender the sender to process
* @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.

View File

@ -1,25 +1,23 @@
package fr.xephi.authme.permission;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.permission.handlers.BPermissionsHandler;
import fr.xephi.authme.permission.handlers.GroupManagerHandler;
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.PermissionsExHandler;
import fr.xephi.authme.permission.handlers.VaultHandler;
import fr.xephi.authme.permission.handlers.ZPermissionsHandler;
import fr.xephi.authme.util.StringUtils;
import net.milkbowl.vault.permission.Permission;
import org.anjocaido.groupmanager.GroupManager;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
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.inject.Inject;
@ -38,7 +36,7 @@ import java.util.List;
* @author Tim Visée, http://timvisee.com
* @version 0.3
*/
public class PermissionsManager {
public class PermissionsManager implements Reloadable {
private final Server server;
private final PluginManager pluginManager;
@ -74,90 +72,17 @@ public class PermissionsManager {
* Setup and hook into the permissions systems.
*/
@PostConstruct
public void setup() {
// Force-unhook from current hooked permissions systems
unhook();
private void setup() {
// Loop through all the available permissions system types
for (PermissionsSystemType type : PermissionsSystemType.values()) {
// Try to find and hook the current plugin if available, print an error if failed
try {
// Try to find the plugin for the current permissions system
Plugin plugin = pluginManager.getPlugin(type.getPluginName());
// Make sure a plugin with this name was found
if (plugin == null)
continue;
// Make sure the plugin is enabled before hooking
if (!plugin.isEnabled()) {
ConsoleLogger.info("Not hooking into " + type.getName() + " because it's disabled!");
continue;
PermissionHandler handler = getPermissionHandler(type);
if (handler != null) {
// Show a success message and return
this.handler = handler;
ConsoleLogger.info("Hooked into " + type.getName() + "!");
return;
}
// 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) {
// An error occurred, show a warning message
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!");
}
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.
*/
public void unhook() {
private void unhook() {
// Reset the current used permissions system
this.handler = null;
@ -182,11 +139,12 @@ public class PermissionsManager {
/**
* Reload the permissions manager, and re-hook all permission plugins.
*/
@Override
public void reload() {
// Unhook all permission plugins
unhook();
// Set up the permissions manager again, return the result
// Set up the permissions manager again
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
* 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);
}
/**
* 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.
* 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.
*/
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.

View File

@ -27,6 +27,11 @@ public class BPermissionsHandler implements PermissionHandler {
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
public boolean isInGroup(Player player, String 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());
}
@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
public boolean isInGroup(Player player, String group) {
final AnjoPermissionsHandler handler = groupManager.getWorldsHolder().getWorldPermissions(player);

View File

@ -38,6 +38,17 @@ public interface PermissionHandler {
*/
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.
*

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());
}
@Override
public boolean hasPermissionOffline(String name, PermissionNode node) {
return false;
}
@Override
public boolean isInGroup(Player player, String group) {
List<String> groupNames = getGroups(player);

View File

@ -1,6 +1,5 @@
package fr.xephi.authme.permission.handlers;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsSystemType;
import org.bukkit.entity.Player;
@ -15,14 +14,16 @@ public class PermissionsExHandler implements PermissionHandler {
private PermissionManager permissionManager;
public PermissionsExHandler(PermissionManager permissionManager) {
this.permissionManager = permissionManager;
public PermissionsExHandler() throws PermissionHandlerException {
permissionManager = PermissionsEx.getPermissionManager();
if (permissionManager == null) {
throw new PermissionHandlerException("Could not get manager of PermissionsEx");
}
}
@Override
public boolean addToGroup(Player player, String 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;
}
@ -42,6 +43,12 @@ public class PermissionsExHandler implements PermissionHandler {
return user.has(node.getNode());
}
@Override
public boolean hasPermissionOffline(String name, PermissionNode node) {
PermissionUser user = permissionManager.getUser(name);
return user.has(node.getNode());
}
@Override
public boolean isInGroup(Player player, String group) {
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.PermissionsSystemType;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.plugin.RegisteredServiceProvider;
import java.util.Arrays;
import java.util.List;
@ -12,8 +14,24 @@ public class VaultHandler implements PermissionHandler {
private Permission vaultProvider;
public VaultHandler(Permission vaultProvider) {
this.vaultProvider = vaultProvider;
public VaultHandler(Server server) throws PermissionHandlerException {
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
@ -31,6 +49,11 @@ public class VaultHandler implements PermissionHandler {
return vaultProvider.has(player, node.getNode());
}
@Override
public boolean hasPermissionOffline(String name, PermissionNode node) {
return vaultProvider.has("", name, node.getNode());
}
@Override
public boolean isInGroup(Player player, String group) {
return vaultProvider.playerInGroup(player, group);

View File

@ -14,7 +14,12 @@ public class ZPermissionsHandler implements PermissionHandler {
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;
}
@ -37,6 +42,15 @@ public class ZPermissionsHandler implements PermissionHandler {
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
public boolean isInGroup(Player player, String 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.unregister.AsynchronousUnregister;
import fr.xephi.authme.util.BukkitService;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
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() {
@Override
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() {
@Override
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