mirror of
https://github.com/AuthMe/AuthMeReloaded.git
synced 2024-12-18 14:47:47 +01:00
Merge branch 'master' of https://github.com/AuthMe/AuthMeReloaded into delay-commands-event
This commit is contained in:
commit
2e5fdb4ca0
52
.circleci/config.yml
Normal file
52
.circleci/config.yml
Normal file
@ -0,0 +1,52 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build_and_test_jdk8:
|
||||
working_directory: ~/authmereloaded-jdk8
|
||||
docker:
|
||||
- image: circleci/openjdk:8-jdk
|
||||
environment:
|
||||
MAVEN_OPTS: -Xmx2048m
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- authmereloaded-{{ checksum "pom.xml" }}
|
||||
- authmereloaded-
|
||||
- run: mvn -T 2 -B dependency:go-offline
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.m2
|
||||
key: authmereloaded-{{ checksum "pom.xml" }}
|
||||
- run: mvn -T 2 -B package
|
||||
- store_test_results:
|
||||
path: target/surefire-reports
|
||||
- store_artifacts:
|
||||
path: target/*.jar
|
||||
build_and_test_jdk10:
|
||||
working_directory: ~/authmereloaded-jdk10
|
||||
docker:
|
||||
- image: circleci/openjdk:10-jdk
|
||||
environment:
|
||||
MAVEN_OPTS: -Xmx2048m
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- authmereloaded-{{ checksum "pom.xml" }}
|
||||
- authmereloaded-
|
||||
- run: mvn -T 2 -B dependency:go-offline
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.m2
|
||||
key: authmereloaded-{{ checksum "pom.xml" }}
|
||||
- run: mvn -T 2 -B package
|
||||
- store_test_results:
|
||||
path: target/surefire-reports
|
||||
- store_artifacts:
|
||||
path: target/*.jar
|
||||
workflows:
|
||||
version: 2
|
||||
build_and_test:
|
||||
jobs:
|
||||
- build_and_test_jdk8
|
||||
- build_and_test_jdk10
|
2
.github/ISSUE_TEMPLATE.MD
vendored
2
.github/ISSUE_TEMPLATE.MD
vendored
@ -19,7 +19,7 @@ Standalone server/Bungeecord network, SQLite/MySql, ...
|
||||
This can be found by running `/authme version`
|
||||
|
||||
### Error Log:
|
||||
Pastebin/Hastebin/Gist link of the error logo or stacktrace (if any)
|
||||
Pastebin/Hastebin/Gist link of the error log or stacktrace (if any)
|
||||
|
||||
### Configuration:
|
||||
Pastebin/Hastebin/Gist link of your config.yml file (remember to delete any sensitive data)
|
||||
|
25
.travis.yml
25
.travis.yml
@ -1,25 +0,0 @@
|
||||
sudo: required
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- oracle-java8-installer
|
||||
- git
|
||||
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
- oraclejdk9
|
||||
|
||||
before_script:
|
||||
- "sudo git clone https://www.github.com/P-H-C/phc-winner-argon2.git argon2-src"
|
||||
- "cd argon2-src && sudo make && sudo make install && cd .."
|
||||
|
||||
script: mvn clean verify -B
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/952357dbd9d3cea70fd5
|
||||
on_success: change # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: false # default: false
|
115
README.md
115
README.md
@ -1,54 +1,15 @@
|
||||
<p align="center"><img src="http://i63.tinypic.com/rtp06o.png"></p>
|
||||
<p align="center"><strong>The most used authentication plugin for the Spigot and derivates!</strong></p>
|
||||
<hr>
|
||||
# AuthMeReloaded
|
||||
**"The best authentication plugin for the Bukkit modding API!"**
|
||||
<img src="http://i63.tinypic.com/rtp06o.png" alt="AuthMeLogo" style="width: 250px;"/>
|
||||
|
||||
##### Links and Contacts:
|
||||
| Type | Badges |
|
||||
|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **General:** | ![](https://tokei.rs/b1/github/AuthMe/AuthMeReloaded?category=code) ![](https://tokei.rs/b1/github/AuthMe/AuthMeReloaded?category=files) |
|
||||
| **Code quality:** | [![Code Climate](https://codeclimate.com/github/AuthMe/AuthMeReloaded/badges/gpa.svg)](https://codeclimate.com/github/AuthMe/AuthMeReloaded) [![Coverage status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master) |
|
||||
| **Jenkins CI:** | [![Jenkins Status](https://img.shields.io/website-up-down-green-red/http/shields.io.svg?label=ci.codemc.org)](https://ci.codemc.org/) [![Build Status](https://ci.codemc.org/buildStatus/icon?job=AuthMe/AuthMeReloaded)](https://ci.codemc.org/job/AuthMe/job/AuthMeReloaded) ![Build Tests](https://img.shields.io/jenkins/t/https/ci.codemc.org/job/AuthMe/job/AuthMeReloaded.svg) |
|
||||
| **Other CIs:** | [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded) |
|
||||
|
||||
- Support:
|
||||
- [GitHub issue tracker](https://github.com/AuthMe/AuthMeReloaded/issues)
|
||||
- [BukkitDev page](https://dev.bukkit.org/projects/authme-reloaded)
|
||||
- [Spigot page](https://www.spigotmc.org/resources/authmereloaded.6269/)
|
||||
- [Discord](https://discord.gg/Vn9eCyE)
|
||||
|
||||
- CI Services:
|
||||
- [Official Jenkins](http://ci.xephi.fr/job/AuthMeReloaded) (**DEVELOPMENT BUILDS**)
|
||||
- Travis CI: [![Travis CI](https://travis-ci.org/AuthMe/AuthMeReloaded.svg?branch=master)](https://travis-ci.org/AuthMe/AuthMeReloaded)
|
||||
- CircleCI: [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded)
|
||||
|
||||
- Project status:
|
||||
- Test coverage: [![Coverage status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master)
|
||||
- Code climate: [![Code Climate](https://codeclimate.com/github/AuthMe/AuthMeReloaded/badges/gpa.svg)](https://codeclimate.com/github/AuthMe/AuthMeReloaded)
|
||||
|
||||
- Development resources:
|
||||
- <a href="http://ci.xephi.fr/job/AuthMeReloaded/javadoc/">JavaDocs</a>
|
||||
- <a href="http://ci.xephi.fr/plugin/repository/everything/">Maven Repository</a>
|
||||
|
||||
- Statistics:
|
||||
- bStats: [AuthMe on bstats.org](https://bstats.org/plugin/bukkit/AuthMe)
|
||||
|
||||
<hr>
|
||||
|
||||
##### Compiling requirements:
|
||||
>- JDK 1.8
|
||||
>- Maven
|
||||
>- Git/Github (Optional)
|
||||
|
||||
##### How to compile the project:
|
||||
>- Clone the project with Git/Github
|
||||
>- Execute command "mvn clean package"
|
||||
|
||||
##### Running requirements:
|
||||
>- Java 1.8
|
||||
>- TacoSpigot, PaperSpigot or Spigot (1.7.10, 1.8.X, 1.9.X, 1.10.X, 1.11.X, 1.12.X)<br>
|
||||
(In case you use Thermos, Cauldron or similar, you have to update the SpecialSource library to support Java 8 plugins.
|
||||
HowTo: https://github.com/games647/FastLogin/issues/111#issuecomment-272331347)
|
||||
>- ProtocolLib (optional, required by some features)
|
||||
|
||||
<hr>
|
||||
|
||||
### Plugin Description:
|
||||
|
||||
##### "The best authentication plugin for the Bukkit/Spigot API!"
|
||||
## Description
|
||||
|
||||
Prevent username stealing on your server!<br>
|
||||
Use it to secure your Offline mode server or to increase your Online mode server's protection!
|
||||
@ -65,7 +26,7 @@ You can also create your own translation file and, if you want, you can share it
|
||||
<ul>
|
||||
<li><strong>E-Mail Recovery System !!!</strong></li>
|
||||
<li>Username spoofing protection.</li>
|
||||
<li>Countries Whitelist/Blacklist! <a href="http://dev.maxmind.com/geoip/legacy/codes/iso3166/">(country codes)</a></li>
|
||||
<li>Countries Whitelist/Blacklist! <a href="https://dev.maxmind.com/geoip/legacy/codes/iso3166/">(country codes)</a></li>
|
||||
<li><strong>Built-in AntiBot System!</strong></li>
|
||||
<li><strong>ForceLogin Feature: Admins can login with all account via console command!</strong></li>
|
||||
<li><strong>Avoid the "Logged in from another location" message!</strong></li>
|
||||
@ -117,15 +78,51 @@ You can also create your own translation file and, if you want, you can share it
|
||||
- [How to convert from Rakamak](https://dev.bukkit.org/projects/authme-reloaded/pages/how-to-import-database-from-rakamak)
|
||||
- Convert between database types (e.g. SQLite to MySQL): /authme converter
|
||||
|
||||
<hr>
|
||||
|
||||
##### Sponsor
|
||||
[GameHosting.it](http://www.gamehosting.it) is leader in Italy as Game Server Provider. With its own DataCenter offers Anti-DDoS solutions at affordable prices. Game Server of Minecraft based on Multicraft are equipped with the latest technology in hardware.
|
||||
## Links and Contacts
|
||||
|
||||
##### Credits
|
||||
<p>Contributors: <a href="https://github.com/AuthMe/AuthMeReloaded/wiki/Development-team">developers</a>, <a href="https://github.com/AuthMe/AuthMeReloaded/wiki/Translators">translators</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>
|
||||
- **Support:**
|
||||
- [GitHub issue tracker](https://github.com/AuthMe/AuthMeReloaded/issues)
|
||||
- [BukkitDev page](https://dev.bukkit.org/projects/authme-reloaded)
|
||||
- [Spigot page](https://www.spigotmc.org/resources/authmereloaded.6269/)
|
||||
- [Discord](https://discord.gg/Vn9eCyE)
|
||||
|
||||
##### GeoIP License
|
||||
This product uses data from the GeoLite API created by MaxMind, available at http://www.maxmind.com
|
||||
- **Dev resources:**
|
||||
- <a href="https://ci.codemc.org/job/AuthMe/job/AuthMeReloaded/javadoc/">JavaDocs</a>
|
||||
- <a href="http://repo.codemc.org/repository/maven-public/">Maven Repository</a>
|
||||
|
||||
- **Statistics:**
|
||||
![Graph](https://bstats.org/signatures/bukkit/AuthMe.svg)
|
||||
|
||||
## Requirements
|
||||
|
||||
##### Compiling requirements:
|
||||
>- JDK 1.8
|
||||
>- Maven
|
||||
>- Git/Github (Optional)
|
||||
|
||||
##### How to compile the project:
|
||||
>- Clone the project with Git/Github
|
||||
>- Execute command "mvn clean package"
|
||||
|
||||
##### Running requirements:
|
||||
>- Java 1.8
|
||||
>- TacoSpigot, PaperSpigot or Spigot (1.7.10, 1.8.X, 1.9.X, 1.10.X, 1.11.X, 1.12.X)<br>
|
||||
(In case you use Thermos, Cauldron or similar, you have to update the SpecialSource library to support Java 8 plugins.
|
||||
HowTo: https://github.com/games647/FastLogin/issues/111#issuecomment-272331347)
|
||||
>- ProtocolLib (optional, required by some features)
|
||||
|
||||
## Credits
|
||||
|
||||
##### Sponsor:
|
||||
[FastVM.io](https://fastvm.io) is leader in VPS hosting solutions. With its own DataCenter offers Anti-DDoS solutions at affordable prices.
|
||||
|
||||
##### Contributors:
|
||||
Team members: <a href="https://github.com/AuthMe/AuthMeReloaded/wiki/Development-team">developers</a>, <a href="https://github.com/AuthMe/AuthMeReloaded/wiki/Translators">translators</a>
|
||||
|
||||
Credits for the old version of the plugin: d4rkwarriors, fabe1337, Whoami2 and pomo4ka
|
||||
|
||||
Thanks also to: AS1LV3RN1NJA, Hoeze and eprimex
|
||||
|
||||
##### GeoIP License:
|
||||
This product uses data from the GeoLite API created by MaxMind, available at https://www.maxmind.com
|
||||
|
20
circle.yml
20
circle.yml
@ -1,20 +0,0 @@
|
||||
machine:
|
||||
java:
|
||||
version: oraclejdk8
|
||||
dependencies:
|
||||
pre:
|
||||
- "sudo apt-get update; sudo apt-get install -y git; sudo git clone https://www.github.com/P-H-C/phc-winner-argon2.git argon2-src; cd argon2-src; sudo make; sudo make install"
|
||||
general:
|
||||
artifacts:
|
||||
- "target/AuthMe-*.jar"
|
||||
test:
|
||||
override:
|
||||
- mvn clean install -B
|
||||
post:
|
||||
- cp ./target/AuthMe-*.jar $CIRCLE_ARTIFACTS
|
||||
- cp ./target/AuthMe-*.jar $CIRCLE_ARTIFACTS/AuthMe.jar
|
||||
- mkdir -p $CIRCLE_TEST_REPORTS/junit/
|
||||
- find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
|
||||
notify:
|
||||
webhooks:
|
||||
- url: https://webhooks.gitter.im/e/7b92ac1a1741748b26bf
|
@ -1,5 +1,5 @@
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
<!-- File auto-generated on Fri Dec 01 19:16:15 CET 2017. See docs/commands/commands.tpl.md -->
|
||||
<!-- File auto-generated on Sun Apr 22 11:00:10 CEST 2018. See docs/commands/commands.tpl.md -->
|
||||
|
||||
## AuthMe Commands
|
||||
You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >`
|
||||
@ -49,7 +49,7 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
|
||||
- **/authme version**: Show detailed information about the installed AuthMeReloaded version, the developers, contributors, and license.
|
||||
- **/authme converter** [job]: Converter command for AuthMeReloaded.
|
||||
<br />Requires `authme.admin.converter`
|
||||
- **/authme messages** [help]: Adds missing messages to the current messages file.
|
||||
- **/authme messages**: Adds missing texts to the current help messages file.
|
||||
<br />Requires `authme.admin.updatemessages`
|
||||
- **/authme recent**: Shows the last players that have logged in.
|
||||
<br />Requires `authme.admin.seerecent`
|
||||
@ -85,6 +85,15 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
|
||||
- **/changepassword** <oldPassword> <newPassword>: Command to change your password using AuthMeReloaded.
|
||||
<br />Requires `authme.player.changepassword`
|
||||
- **/changepassword help** [query]: View detailed help for /changepassword commands.
|
||||
- **/totp**: Performs actions related to two-factor authentication.
|
||||
- **/totp code** <code>: Processes the two-factor authentication code during login.
|
||||
- **/totp add**: Enables two-factor authentication for your account.
|
||||
<br />Requires `authme.player.totpadd`
|
||||
- **/totp confirm** <code>: Saves the generated TOTP secret after confirmation.
|
||||
<br />Requires `authme.player.totpadd`
|
||||
- **/totp remove** <code>: Disables two-factor authentication for your account.
|
||||
<br />Requires `authme.player.totpremove`
|
||||
- **/totp help** [query]: View detailed help for /totp commands.
|
||||
- **/captcha** <captcha>: Captcha command for AuthMeReloaded.
|
||||
<br />Requires `authme.player.captcha`
|
||||
- **/captcha help** [query]: View detailed help for /captcha commands.
|
||||
@ -95,4 +104,4 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
|
||||
|
||||
---
|
||||
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Dec 01 19:16:15 CET 2017
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 22 11:00:10 CEST 2018
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
<!-- File auto-generated on Sun Jan 21 18:49:44 CET 2018. See docs/config/config.tpl.md -->
|
||||
<!-- File auto-generated on Mon May 21 09:08:25 CEST 2018. See docs/config/config.tpl.md -->
|
||||
|
||||
## AuthMe Configuration
|
||||
The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder,
|
||||
@ -37,12 +37,16 @@ DataSource:
|
||||
mySQLRealName: 'realname'
|
||||
# Column for storing players passwords
|
||||
mySQLColumnPassword: 'password'
|
||||
# Column for storing players passwords salts
|
||||
mySQLColumnSalt: ''
|
||||
# Column for storing players emails
|
||||
mySQLColumnEmail: 'email'
|
||||
# Column for storing if a player is logged in or not
|
||||
mySQLColumnLogged: 'isLogged'
|
||||
# Column for storing if a player has a valid session or not
|
||||
mySQLColumnHasSession: 'hasSession'
|
||||
# Column for storing a player's TOTP key (for two-factor authentication)
|
||||
mySQLtotpKey: 'totp'
|
||||
# Column for storing the player's last IP
|
||||
mySQLColumnIp: 'ip'
|
||||
# Column for storing players lastlogins
|
||||
@ -69,8 +73,6 @@ DataSource:
|
||||
# You should set this at least 30 seconds less than mysql server wait_timeout
|
||||
maxLifetime: 1800
|
||||
ExternalBoardOptions:
|
||||
# Column for storing players passwords salts
|
||||
mySQLColumnSalt: ''
|
||||
# Column for storing players groups
|
||||
mySQLColumnGroup: ''
|
||||
# -1 means disabled. If you want that only activated players
|
||||
@ -138,6 +140,8 @@ settings:
|
||||
- '/reg'
|
||||
- '/email'
|
||||
- '/captcha'
|
||||
- '/2fa'
|
||||
- '/totp'
|
||||
# Max number of allowed registrations per IP
|
||||
# The value 0 means an unlimited number of registrations!
|
||||
maxRegPerIp: 1
|
||||
@ -384,7 +388,7 @@ Protection:
|
||||
# Apply the protection also to registered usernames
|
||||
enableProtectionRegistered: true
|
||||
# Countries allowed to join the server and register. For country codes, see
|
||||
# http://dev.maxmind.com/geoip/legacy/codes/iso3166/
|
||||
# https://dev.maxmind.com/geoip/legacy/codes/iso3166/
|
||||
# PLEASE USE QUOTES!
|
||||
countries:
|
||||
- 'US'
|
||||
@ -404,6 +408,9 @@ Protection:
|
||||
antiBotDuration: 10
|
||||
# Delay in seconds before the antibot activation
|
||||
antiBotDelay: 60
|
||||
quickCommands:
|
||||
# Kicks the player that issued a command before the defined time after the join process
|
||||
denyCommandsBeforeMilliseconds: 1000
|
||||
Purge:
|
||||
# If enabled, AuthMe automatically purges old, unused accounts
|
||||
useAutoPurge: false
|
||||
@ -555,4 +562,4 @@ To change settings on a running server, save your changes to config.yml and use
|
||||
|
||||
---
|
||||
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Jan 21 18:49:44 CET 2018
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Mon May 21 09:08:25 CEST 2018
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
<!-- File auto-generated on Fri Dec 01 19:16:17 CET 2017. See docs/permissions/permission_nodes.tpl.md -->
|
||||
<!-- File auto-generated on Mon May 21 08:43:08 CEST 2018. See docs/permissions/permission_nodes.tpl.md -->
|
||||
|
||||
## AuthMe Permission Nodes
|
||||
The following are the permission nodes that are currently supported by the latest dev builds.
|
||||
@ -30,6 +30,7 @@ The following are the permission nodes that are currently supported by the lates
|
||||
- **authme.admin.switchantibot** – Administrator command to toggle the AntiBot protection status.
|
||||
- **authme.admin.unregister** – Administrator command to unregister an existing user.
|
||||
- **authme.admin.updatemessages** – Permission to use the update messages command.
|
||||
- **authme.allowchatbeforelogin** – Permission to send chat messages before being logged in.
|
||||
- **authme.allowmultipleaccounts** – Permission to be able to register multiple accounts.
|
||||
- **authme.bypassantibot** – Permission node to bypass AntiBot protection.
|
||||
- **authme.bypasscountrycheck** – Permission to bypass the GeoIp country code check.
|
||||
@ -57,13 +58,16 @@ The following are the permission nodes that are currently supported by the lates
|
||||
- **authme.player.email.see** – Command permission to see the own email address.
|
||||
- **authme.player.login** – Command permission to login.
|
||||
- **authme.player.logout** – Command permission to logout.
|
||||
- **authme.player.protection.quickcommandsprotection** – Permission that enables on join quick commands checks for the player.
|
||||
- **authme.player.register** – Command permission to register.
|
||||
- **authme.player.security.verificationcode** – Permission to use the email verification codes feature.
|
||||
- **authme.player.seeownaccounts** – Permission to use to see own other accounts.
|
||||
- **authme.player.totpadd** – Permission to enable two-factor authentication.
|
||||
- **authme.player.totpremove** – Permission to disable two-factor authentication.
|
||||
- **authme.player.unregister** – Command permission to unregister.
|
||||
- **authme.vip** – When the server is full and someone with this permission joins the server, someone will be kicked.
|
||||
|
||||
|
||||
---
|
||||
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Dec 01 19:16:17 CET 2017
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Mon May 21 08:43:08 CEST 2018
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
<!-- File auto-generated on Sun Jan 14 11:14:59 CET 2018. See docs/translations/translations.tpl.md -->
|
||||
<!-- File auto-generated on Mon Jun 25 21:53:35 CEST 2018. See docs/translations/translations.tpl.md -->
|
||||
|
||||
# AuthMe Translations
|
||||
The following translations are available in AuthMe. Set `messagesLanguage` to the language code
|
||||
@ -8,37 +8,37 @@ in your config.yml to use the language, or use another language code to start a
|
||||
Code | Language | Translated |
|
||||
---- | -------- | ---------: | ------
|
||||
[en](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_en.yml) | English | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" />
|
||||
[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 86% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=99bb22&w=86&h=5&txtpad=1" alt="bar" />
|
||||
[br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 90% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=90&h=5&txtpad=1" alt="bar" />
|
||||
[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 90% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=90&h=5&txtpad=1" alt="bar" />
|
||||
[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 90% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=90&h=5&txtpad=1" alt="bar" />
|
||||
[eo](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eo.yml) | Esperanto | 90% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=90&h=5&txtpad=1" alt="bar" />
|
||||
[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 98% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=98&h=5&txtpad=1" alt="bar" />
|
||||
[et](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_et.yml) | Estonian | 90% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=90&h=5&txtpad=1" alt="bar" />
|
||||
[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 48% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aa5500&w=48&h=5&txtpad=1" alt="bar" />
|
||||
[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 51% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb6600&w=51&h=5&txtpad=1" alt="bar" />
|
||||
[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" />
|
||||
[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 54% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb6600&w=54&h=5&txtpad=1" alt="bar" />
|
||||
[hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 98% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=98&h=5&txtpad=1" alt="bar" />
|
||||
[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 53% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb6600&w=53&h=5&txtpad=1" alt="bar" />
|
||||
[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 98% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=98&h=5&txtpad=1" alt="bar" />
|
||||
[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 98% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=98&h=5&txtpad=1" alt="bar" />
|
||||
[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Lithuanian | 40% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aa4400&w=40&h=5&txtpad=1" alt="bar" />
|
||||
[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 90% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=90&h=5&txtpad=1" alt="bar" />
|
||||
[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 98% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=98&h=5&txtpad=1" alt="bar" />
|
||||
[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 90% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=90&h=5&txtpad=1" alt="bar" />
|
||||
[ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 90% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=90&h=5&txtpad=1" alt="bar" />
|
||||
[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 98% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=98&h=5&txtpad=1" alt="bar" />
|
||||
[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 90% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=90&h=5&txtpad=1" alt="bar" />
|
||||
[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 86% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=99bb22&w=86&h=5&txtpad=1" alt="bar" />
|
||||
[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 71% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=71&h=5&txtpad=1" alt="bar" />
|
||||
[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 87% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=99bb22&w=87&h=5&txtpad=1" alt="bar" />
|
||||
[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 98% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=98&h=5&txtpad=1" alt="bar" />
|
||||
[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 90% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=90&h=5&txtpad=1" alt="bar" />
|
||||
[zhmc](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhmc.yml) | Chinese (Macau) | 73% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=73&h=5&txtpad=1" alt="bar" />
|
||||
[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 98% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=98&h=5&txtpad=1" alt="bar" />
|
||||
[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 76% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb9900&w=76&h=5&txtpad=1" alt="bar" />
|
||||
[br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 80% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=80&h=5&txtpad=1" alt="bar" />
|
||||
[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 80% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=80&h=5&txtpad=1" alt="bar" />
|
||||
[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 80% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=80&h=5&txtpad=1" alt="bar" />
|
||||
[eo](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eo.yml) | Esperanto | 80% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=80&h=5&txtpad=1" alt="bar" />
|
||||
[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
|
||||
[et](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_et.yml) | Estonian | 80% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=80&h=5&txtpad=1" alt="bar" />
|
||||
[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 42% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aa5500&w=42&h=5&txtpad=1" alt="bar" />
|
||||
[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 45% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aa5500&w=45&h=5&txtpad=1" alt="bar" />
|
||||
[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
|
||||
[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 48% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aa5500&w=48&h=5&txtpad=1" alt="bar" />
|
||||
[hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 87% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=99bb22&w=87&h=5&txtpad=1" alt="bar" />
|
||||
[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 47% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aa5500&w=47&h=5&txtpad=1" alt="bar" />
|
||||
[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" />
|
||||
[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
|
||||
[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Lithuanian | 36% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aa4400&w=36&h=5&txtpad=1" alt="bar" />
|
||||
[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 80% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=80&h=5&txtpad=1" alt="bar" />
|
||||
[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" />
|
||||
[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" />
|
||||
[ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 80% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=80&h=5&txtpad=1" alt="bar" />
|
||||
[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
|
||||
[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 80% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=80&h=5&txtpad=1" alt="bar" />
|
||||
[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 76% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb9900&w=76&h=5&txtpad=1" alt="bar" />
|
||||
[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 63% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb7700&w=63&h=5&txtpad=1" alt="bar" />
|
||||
[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 77% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb9900&w=77&h=5&txtpad=1" alt="bar" />
|
||||
[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
|
||||
[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 80% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=80&h=5&txtpad=1" alt="bar" />
|
||||
[zhmc](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhmc.yml) | Chinese (Macau) | 65% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb7700&w=65&h=5&txtpad=1" alt="bar" />
|
||||
[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 87% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=99bb22&w=87&h=5&txtpad=1" alt="bar" />
|
||||
|
||||
|
||||
---
|
||||
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Jan 14 11:14:59 CET 2018
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Mon Jun 25 21:53:35 CEST 2018
|
||||
|
287
pom.xml
287
pom.xml
@ -27,7 +27,7 @@
|
||||
|
||||
<ciManagement>
|
||||
<system>jenkins</system>
|
||||
<url>http://ci.xephi.fr/job/AuthMeReloaded/</url>
|
||||
<url>http://ci.codemc.org/job/AuthMe/job/AuthMeReloaded/</url>
|
||||
</ciManagement>
|
||||
|
||||
<issueManagement>
|
||||
@ -37,11 +37,11 @@
|
||||
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>nexus-snapshots</id>
|
||||
<id>codemc-snapshots</id>
|
||||
<url>https://repo.codemc.org/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>nexus-releases</id>
|
||||
<id>codemc-releases</id>
|
||||
<url>https://repo.codemc.org/repository/maven-releases/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
@ -57,7 +57,8 @@
|
||||
<properties>
|
||||
<!-- Project properties -->
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.jdkVersion>1.8</project.jdkVersion>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<jdk.version>1.8</jdk.version>
|
||||
|
||||
<!-- Output properties -->
|
||||
<project.outputName>AuthMe</project.outputName>
|
||||
@ -75,6 +76,10 @@
|
||||
<bukkit.version>1.12.2-R0.1-SNAPSHOT</bukkit.version>
|
||||
</properties>
|
||||
|
||||
<prerequisites>
|
||||
<maven>3.3.9</maven>
|
||||
</prerequisites>
|
||||
|
||||
<!-- Jenkins profile -->
|
||||
<profiles>
|
||||
<!-- Set the buildNumber using the jenkins env. variable -->
|
||||
@ -104,7 +109,8 @@
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<finalName>${project.outputName}-${project.version}</finalName>
|
||||
<defaultGoal>clean install</defaultGoal>
|
||||
<finalName>${project.outputName}-${project.version}-noshade</finalName>
|
||||
|
||||
<resources>
|
||||
<resource>
|
||||
@ -126,36 +132,107 @@
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<!-- Maven Java Compiler -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<configuration>
|
||||
<source>${project.jdkVersion}</source>
|
||||
<target>${project.jdkVersion}</target>
|
||||
<source>${jdk.version}</source>
|
||||
<target>${jdk.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Test Plugin -->
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>pre-unit-test</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>post-unit-test</id>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.20.1</version>
|
||||
<version>2.21.0</version>
|
||||
<configuration>
|
||||
<!-- Force the right file encoding during unit testing -->
|
||||
<argLine>-Dfile.encoding=${project.build.sourceEncoding} @{argLine}</argLine>
|
||||
<!-- Set language to English in order to get consistent results for localized time formatting -->
|
||||
<argLine>-Dfile.encoding=${project.build.sourceEncoding} -Duser.language=en @{argLine}</argLine>
|
||||
<systemPropertyVariables>
|
||||
<project.skipExtendedHashTests>${project.skipExtendedHashTests}</project.skipExtendedHashTests>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Libs Shading and Relocation -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadoc</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>aggregate-javadoc</id>
|
||||
<goals>
|
||||
<goal>aggregate</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<finalName>${project.outputName}-${project.version}</finalName>
|
||||
<show>public</show>
|
||||
<failOnError>false</failOnError>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<finalName>${project.outputName}-${project.version}</finalName>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.1.1</version>
|
||||
<executions>
|
||||
<!-- Run shade goal on package phase -->
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
@ -165,7 +242,9 @@
|
||||
</executions>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||
<finalName>${project.outputName}-${project.version}</finalName>
|
||||
|
||||
<!--
|
||||
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)
|
||||
@ -174,31 +253,31 @@
|
||||
<!-- Include all google libraries, because they are not available before 1.12 -->
|
||||
<relocation>
|
||||
<pattern>com.google</pattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.google</shadedPattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.com.google</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>ch.jalu.injector</pattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.jalu.injector</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>ch.jalu.configme</pattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.jalu.configme</shadedPattern>
|
||||
<pattern>ch.jalu</pattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.ch.jalu</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.zaxxer.hikari</pattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.zaxxer.hikari</shadedPattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.com.zaxxer.hikari</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.slf4j</pattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.slf4j.slf4j</shadedPattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.org.slf4j</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.maxmind.geoip</pattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.maxmind.geoip</shadedPattern>
|
||||
<pattern>com.maxmind.db</pattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.com.maxmind.db</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.ice.tar</pattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.com.icetar.tar</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>net.ricecode.similarity</pattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.ricecode.similarity</shadedPattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.ricecode.net.ricecode.similarity</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>de.rtner</pattern>
|
||||
@ -208,6 +287,10 @@
|
||||
<pattern>de.mkammerer</pattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.de.mkammerer</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.warrenstrange</pattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.com.warrenstrange</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>javax.inject</pattern>
|
||||
<shadedPattern>fr.xephi.authme.libs.javax.inject</shadedPattern>
|
||||
@ -220,58 +303,24 @@
|
||||
</relocations>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Coverage report generator -->
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prepare-agent</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Coverage report uploader -->
|
||||
<plugin>
|
||||
<groupId>org.eluder.coveralls</groupId>
|
||||
<artifactId>coveralls-maven-plugin</artifactId>
|
||||
<version>4.3.0</version>
|
||||
<configuration>
|
||||
<failOnServiceError>false</failOnServiceError>
|
||||
</configuration>
|
||||
<!-- The secret token is provided with a command-line parameter! -->
|
||||
</plugin>
|
||||
<!-- JavaDocs generator -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.10.4</version>
|
||||
<configuration>
|
||||
<show>public</show>
|
||||
<failOnError>false</failOnError>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<phase>deploy</phase>
|
||||
<goals><goal>jar</goal></goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.8.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>deploy</id>
|
||||
<phase>deploy</phase>
|
||||
<goals><goal>deploy</goal></goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.eluder.coveralls</groupId>
|
||||
<artifactId>coveralls-maven-plugin</artifactId>
|
||||
<version>4.3.0</version>
|
||||
<configuration>
|
||||
<!-- The secret token is provided with a command-line parameter! -->
|
||||
<failOnServiceError>false</failOnServiceError>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
@ -301,12 +350,6 @@
|
||||
<url>http://repo.onarandombox.com/content/groups/public</url>
|
||||
</repository>
|
||||
|
||||
<!-- LuckPerms Repo -->
|
||||
<repository>
|
||||
<id>luck-repo</id>
|
||||
<url>https://repo.lucko.me/</url>
|
||||
</repository>
|
||||
|
||||
<!-- Vault Repo -->
|
||||
<repository>
|
||||
<id>vault-repo</id>
|
||||
@ -315,8 +358,8 @@
|
||||
|
||||
<!-- Our Repo (Many libs) -->
|
||||
<repository>
|
||||
<id>xephi-repo</id>
|
||||
<url>http://ci.xephi.fr/plugin/repository/everything/</url>
|
||||
<id>codemc-repo</id>
|
||||
<url>https://repo.codemc.org/repository/maven-public/</url>
|
||||
</repository>
|
||||
|
||||
<!-- bStats Repo -->
|
||||
@ -334,7 +377,6 @@
|
||||
<groupId>ch.jalu</groupId>
|
||||
<artifactId>injector</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@ -343,7 +385,6 @@
|
||||
<groupId>net.ricecode</groupId>
|
||||
<artifactId>string-similarity</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@ -351,8 +392,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.2</version>
|
||||
<scope>compile</scope>
|
||||
<version>2.8.5</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@ -360,17 +400,24 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>23.5-jre</version>
|
||||
<scope>compile</scope>
|
||||
<version>25.1-jre</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Maxmind GeoIp API -->
|
||||
<!-- MaxMind GEO IP with our modifications to use GSON in replacement of the big Jackson dependency -->
|
||||
<!-- GSON is already included and therefore it reduces the file size in comparison to the original version -->
|
||||
<dependency>
|
||||
<groupId>com.maxmind.geoip</groupId>
|
||||
<artifactId>geoip-api</artifactId>
|
||||
<version>1.3.1</version>
|
||||
<scope>compile</scope>
|
||||
<groupId>com.maxmind.db</groupId>
|
||||
<artifactId>maxmind-db-gson</artifactId>
|
||||
<version>2.0.2-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Library for tar archives -->
|
||||
<dependency>
|
||||
<groupId>javatar</groupId>
|
||||
<artifactId>javatar</artifactId>
|
||||
<version>2.5</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@ -379,7 +426,6 @@
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-email</artifactId>
|
||||
<version>1.5</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@ -395,22 +441,20 @@
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>2.7.4</version>
|
||||
<scope>compile</scope>
|
||||
<version>3.2.0</version>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<groupId>org.slf4j</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- HikariCP Logger -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>1.7.25</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@ -419,7 +463,6 @@
|
||||
<groupId>de.rtner</groupId>
|
||||
<artifactId>PBKDF2</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@ -427,7 +470,16 @@
|
||||
<dependency>
|
||||
<groupId>de.mkammerer</groupId>
|
||||
<artifactId>argon2-jvm-nolibs</artifactId>
|
||||
<version>2.3</version>
|
||||
<version>2.4</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- TOTP client -->
|
||||
<dependency>
|
||||
<groupId>com.warrenstrange</groupId>
|
||||
<artifactId>googleauth</artifactId>
|
||||
<version>1.1.5</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Spigot API, http://www.spigotmc.org/ -->
|
||||
@ -462,12 +514,25 @@
|
||||
|
||||
<!-- Bukkit Libraries -->
|
||||
|
||||
<!-- ConfigMe -->
|
||||
<dependency>
|
||||
<groupId>ch.jalu</groupId>
|
||||
<artifactId>configme</artifactId>
|
||||
<version>0.4.1</version>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- bStats metrics -->
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bukkit</artifactId>
|
||||
<version>1.2</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@ -475,7 +540,7 @@
|
||||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib-API</artifactId>
|
||||
<version>4.4.0-SNAPSHOT</version>
|
||||
<version>4.3.0</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@ -493,7 +558,7 @@
|
||||
<dependency>
|
||||
<groupId>me.lucko.luckperms</groupId>
|
||||
<artifactId>luckperms-api</artifactId>
|
||||
<version>4.0-SNAPSHOT</version>
|
||||
<version>4.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@ -501,7 +566,7 @@
|
||||
<dependency>
|
||||
<groupId>ru.tehkode</groupId>
|
||||
<artifactId>PermissionsEx</artifactId>
|
||||
<version>1.23.5</version>
|
||||
<version>1.23.5-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@ -736,19 +801,11 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- ConfigMe -->
|
||||
<dependency>
|
||||
<groupId>ch.jalu</groupId>
|
||||
<artifactId>configme</artifactId>
|
||||
<version>0.4.1</version>
|
||||
<scope>compile</scope>
|
||||
<artifactId>datasourcecolumns</artifactId>
|
||||
<version>0.1.1-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Unit Testing Libraries -->
|
||||
@ -771,7 +828,7 @@
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
<version>2.13.0</version>
|
||||
<version>2.19.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
@ -784,13 +841,13 @@
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.21.0.1</version>
|
||||
<version>3.23.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.4.196</version>
|
||||
<version>1.4.197</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
@ -26,11 +26,13 @@ import fr.xephi.authme.service.BackupService;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.MigrationService;
|
||||
import fr.xephi.authme.service.bungeecord.BungeeReceiver;
|
||||
import fr.xephi.authme.service.yaml.YamlParseException;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.SettingsWarner;
|
||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||
import fr.xephi.authme.task.CleanupTask;
|
||||
import fr.xephi.authme.task.purge.PurgeService;
|
||||
import fr.xephi.authme.util.ExceptionUtils;
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.Command;
|
||||
@ -133,7 +135,13 @@ public class AuthMe extends JavaPlugin {
|
||||
try {
|
||||
initialize();
|
||||
} catch (Throwable th) {
|
||||
ConsoleLogger.logException("Aborting initialization of AuthMe:", th);
|
||||
YamlParseException yamlParseException = ExceptionUtils.findThrowableInCause(YamlParseException.class, th);
|
||||
if (yamlParseException == null) {
|
||||
ConsoleLogger.logException("Aborting initialization of AuthMe:", th);
|
||||
} else {
|
||||
ConsoleLogger.logException("File '" + yamlParseException.getFile() + "' contains invalid YAML. "
|
||||
+ "Please run its contents through http://yamllint.com", yamlParseException);
|
||||
}
|
||||
stopOrUnload();
|
||||
return;
|
||||
}
|
||||
@ -148,9 +156,9 @@ public class AuthMe extends JavaPlugin {
|
||||
OnStartupTasks.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!");
|
||||
ConsoleLogger.info("Development builds are available on our jenkins, thanks to FastVM.io");
|
||||
ConsoleLogger.info("Do you want a good vps for your game server? Look at our sponsor FastVM.io leader "
|
||||
+ "as virtual server provider!");
|
||||
|
||||
// Successful message
|
||||
ConsoleLogger.info("AuthMe " + getPluginVersion() + " build n." + getPluginBuildNumber()
|
||||
|
@ -5,7 +5,7 @@ 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;
|
||||
import fr.xephi.authme.util.ExceptionUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
@ -101,7 +101,7 @@ public final class ConsoleLogger {
|
||||
* @param th The Throwable to log
|
||||
*/
|
||||
public static void logException(String message, Throwable th) {
|
||||
warning(message + " " + StringUtils.formatException(th));
|
||||
warning(message + " " + ExceptionUtils.formatException(th));
|
||||
writeLog(Throwables.getStackTraceAsString(th));
|
||||
}
|
||||
|
||||
|
@ -167,7 +167,7 @@ public class AuthMeApi {
|
||||
* @param playerName The name of the player to process
|
||||
*
|
||||
* @return The date of the last login, or null if the player doesn't exist or has never logged in
|
||||
* @Deprecated Use Java 8's Instant method {@link #getLastLoginTime(String)}
|
||||
* @deprecated Use Java 8's Instant method {@link #getLastLoginTime(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public Date getLastLogin(String playerName) {
|
||||
|
@ -12,7 +12,6 @@ import fr.xephi.authme.command.executable.authme.ForceLoginCommand;
|
||||
import fr.xephi.authme.command.executable.authme.GetEmailCommand;
|
||||
import fr.xephi.authme.command.executable.authme.GetIpCommand;
|
||||
import fr.xephi.authme.command.executable.authme.LastLoginCommand;
|
||||
import fr.xephi.authme.command.executable.authme.MessagesCommand;
|
||||
import fr.xephi.authme.command.executable.authme.PurgeBannedPlayersCommand;
|
||||
import fr.xephi.authme.command.executable.authme.PurgeCommand;
|
||||
import fr.xephi.authme.command.executable.authme.PurgeLastPositionCommand;
|
||||
@ -26,6 +25,7 @@ import fr.xephi.authme.command.executable.authme.SetSpawnCommand;
|
||||
import fr.xephi.authme.command.executable.authme.SpawnCommand;
|
||||
import fr.xephi.authme.command.executable.authme.SwitchAntiBotCommand;
|
||||
import fr.xephi.authme.command.executable.authme.UnregisterAdminCommand;
|
||||
import fr.xephi.authme.command.executable.authme.UpdateHelpMessagesCommand;
|
||||
import fr.xephi.authme.command.executable.authme.VersionCommand;
|
||||
import fr.xephi.authme.command.executable.authme.debug.DebugCommand;
|
||||
import fr.xephi.authme.command.executable.captcha.CaptchaCommand;
|
||||
@ -33,13 +33,18 @@ import fr.xephi.authme.command.executable.changepassword.ChangePasswordCommand;
|
||||
import fr.xephi.authme.command.executable.email.AddEmailCommand;
|
||||
import fr.xephi.authme.command.executable.email.ChangeEmailCommand;
|
||||
import fr.xephi.authme.command.executable.email.EmailBaseCommand;
|
||||
import fr.xephi.authme.command.executable.email.EmailSetPasswordCommand;
|
||||
import fr.xephi.authme.command.executable.email.ProcessCodeCommand;
|
||||
import fr.xephi.authme.command.executable.email.RecoverEmailCommand;
|
||||
import fr.xephi.authme.command.executable.email.SetPasswordCommand;
|
||||
import fr.xephi.authme.command.executable.email.ShowEmailCommand;
|
||||
import fr.xephi.authme.command.executable.login.LoginCommand;
|
||||
import fr.xephi.authme.command.executable.logout.LogoutCommand;
|
||||
import fr.xephi.authme.command.executable.register.RegisterCommand;
|
||||
import fr.xephi.authme.command.executable.totp.AddTotpCommand;
|
||||
import fr.xephi.authme.command.executable.totp.ConfirmTotpCommand;
|
||||
import fr.xephi.authme.command.executable.totp.RemoveTotpCommand;
|
||||
import fr.xephi.authme.command.executable.totp.TotpBaseCommand;
|
||||
import fr.xephi.authme.command.executable.totp.TotpCodeCommand;
|
||||
import fr.xephi.authme.command.executable.unregister.UnregisterCommand;
|
||||
import fr.xephi.authme.command.executable.verification.VerificationCommand;
|
||||
import fr.xephi.authme.permission.AdminPermission;
|
||||
@ -55,6 +60,9 @@ import java.util.List;
|
||||
*/
|
||||
public class CommandInitializer {
|
||||
|
||||
private static final boolean OPTIONAL = true;
|
||||
private static final boolean MANDATORY = false;
|
||||
|
||||
private List<CommandDescription> commands;
|
||||
|
||||
public CommandInitializer() {
|
||||
@ -84,7 +92,7 @@ public class CommandInitializer {
|
||||
.labels("login", "l", "log")
|
||||
.description("Login command")
|
||||
.detailedDescription("Command to log in using AuthMeReloaded.")
|
||||
.withArgument("password", "Login password", false)
|
||||
.withArgument("password", "Login password", MANDATORY)
|
||||
.permission(PlayerPermission.LOGIN)
|
||||
.executableCommand(LoginCommand.class)
|
||||
.register();
|
||||
@ -105,8 +113,8 @@ public class CommandInitializer {
|
||||
.labels("register", "reg")
|
||||
.description("Register an account")
|
||||
.detailedDescription("Command to register using AuthMeReloaded.")
|
||||
.withArgument("password", "Password", true)
|
||||
.withArgument("verifyPassword", "Verify password", true)
|
||||
.withArgument("password", "Password", OPTIONAL)
|
||||
.withArgument("verifyPassword", "Verify password", OPTIONAL)
|
||||
.permission(PlayerPermission.REGISTER)
|
||||
.executableCommand(RegisterCommand.class)
|
||||
.register();
|
||||
@ -117,7 +125,7 @@ public class CommandInitializer {
|
||||
.labels("unregister", "unreg")
|
||||
.description("Unregister an account")
|
||||
.detailedDescription("Command to unregister using AuthMeReloaded.")
|
||||
.withArgument("password", "Password", false)
|
||||
.withArgument("password", "Password", MANDATORY)
|
||||
.permission(PlayerPermission.UNREGISTER)
|
||||
.executableCommand(UnregisterCommand.class)
|
||||
.register();
|
||||
@ -128,19 +136,22 @@ public class CommandInitializer {
|
||||
.labels("changepassword", "changepass", "cp")
|
||||
.description("Change password of an account")
|
||||
.detailedDescription("Command to change your password using AuthMeReloaded.")
|
||||
.withArgument("oldPassword", "Old password", false)
|
||||
.withArgument("newPassword", "New password", false)
|
||||
.withArgument("oldPassword", "Old password", MANDATORY)
|
||||
.withArgument("newPassword", "New password", MANDATORY)
|
||||
.permission(PlayerPermission.CHANGE_PASSWORD)
|
||||
.executableCommand(ChangePasswordCommand.class)
|
||||
.register();
|
||||
|
||||
// Create totp base command
|
||||
CommandDescription totpBase = buildTotpBaseCommand();
|
||||
|
||||
// Register the base captcha command
|
||||
CommandDescription captchaBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("captcha")
|
||||
.description("Captcha command")
|
||||
.detailedDescription("Captcha command for AuthMeReloaded.")
|
||||
.withArgument("captcha", "The Captcha", false)
|
||||
.withArgument("captcha", "The Captcha", MANDATORY)
|
||||
.permission(PlayerPermission.CAPTCHA)
|
||||
.executableCommand(CaptchaCommand.class)
|
||||
.register();
|
||||
@ -151,21 +162,13 @@ public class CommandInitializer {
|
||||
.labels("verification")
|
||||
.description("Verification command")
|
||||
.detailedDescription("Command to complete the verification process for AuthMeReloaded.")
|
||||
.withArgument("code", "The code", false)
|
||||
.withArgument("code", "The code", MANDATORY)
|
||||
.permission(PlayerPermission.VERIFICATION_CODE)
|
||||
.executableCommand(VerificationCommand.class)
|
||||
.register();
|
||||
|
||||
List<CommandDescription> baseCommands = ImmutableList.of(
|
||||
authMeBase,
|
||||
emailBase,
|
||||
loginBase,
|
||||
logoutBase,
|
||||
registerBase,
|
||||
unregisterBase,
|
||||
changePasswordBase,
|
||||
captchaBase,
|
||||
verificationBase);
|
||||
List<CommandDescription> baseCommands = ImmutableList.of(authMeBase, emailBase, loginBase, logoutBase,
|
||||
registerBase, unregisterBase, changePasswordBase, totpBase, captchaBase, verificationBase);
|
||||
|
||||
setHelpOnAllBases(baseCommands);
|
||||
commands = baseCommands;
|
||||
@ -191,8 +194,8 @@ public class CommandInitializer {
|
||||
.labels("register", "reg", "r")
|
||||
.description("Register a player")
|
||||
.detailedDescription("Register the specified player with the specified password.")
|
||||
.withArgument("player", "Player name", false)
|
||||
.withArgument("password", "Password", false)
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.withArgument("password", "Password", MANDATORY)
|
||||
.permission(AdminPermission.REGISTER)
|
||||
.executableCommand(RegisterAdminCommand.class)
|
||||
.register();
|
||||
@ -203,7 +206,7 @@ public class CommandInitializer {
|
||||
.labels("unregister", "unreg", "unr")
|
||||
.description("Unregister a player")
|
||||
.detailedDescription("Unregister the specified player.")
|
||||
.withArgument("player", "Player name", false)
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.permission(AdminPermission.UNREGISTER)
|
||||
.executableCommand(UnregisterAdminCommand.class)
|
||||
.register();
|
||||
@ -214,7 +217,7 @@ public class CommandInitializer {
|
||||
.labels("forcelogin", "login")
|
||||
.description("Enforce login player")
|
||||
.detailedDescription("Enforce the specified player to login.")
|
||||
.withArgument("player", "Online player name", true)
|
||||
.withArgument("player", "Online player name", OPTIONAL)
|
||||
.permission(AdminPermission.FORCE_LOGIN)
|
||||
.executableCommand(ForceLoginCommand.class)
|
||||
.register();
|
||||
@ -225,8 +228,8 @@ public class CommandInitializer {
|
||||
.labels("password", "changepassword", "changepass", "cp")
|
||||
.description("Change a player's password")
|
||||
.detailedDescription("Change the password of a player.")
|
||||
.withArgument("player", "Player name", false)
|
||||
.withArgument("pwd", "New password", false)
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.withArgument("pwd", "New password", MANDATORY)
|
||||
.permission(AdminPermission.CHANGE_PASSWORD)
|
||||
.executableCommand(ChangePasswordAdminCommand.class)
|
||||
.register();
|
||||
@ -237,7 +240,7 @@ public class CommandInitializer {
|
||||
.labels("lastlogin", "ll")
|
||||
.description("Player's last login")
|
||||
.detailedDescription("View the date of the specified players last login.")
|
||||
.withArgument("player", "Player name", true)
|
||||
.withArgument("player", "Player name", OPTIONAL)
|
||||
.permission(AdminPermission.LAST_LOGIN)
|
||||
.executableCommand(LastLoginCommand.class)
|
||||
.register();
|
||||
@ -248,7 +251,7 @@ public class CommandInitializer {
|
||||
.labels("accounts", "account")
|
||||
.description("Display player accounts")
|
||||
.detailedDescription("Display all accounts of a player by his player name or IP.")
|
||||
.withArgument("player", "Player name or IP", true)
|
||||
.withArgument("player", "Player name or IP", OPTIONAL)
|
||||
.permission(AdminPermission.ACCOUNTS)
|
||||
.executableCommand(AccountsCommand.class)
|
||||
.register();
|
||||
@ -259,7 +262,7 @@ public class CommandInitializer {
|
||||
.labels("email", "mail", "getemail", "getmail")
|
||||
.description("Display player's email")
|
||||
.detailedDescription("Display the email address of the specified player if set.")
|
||||
.withArgument("player", "Player name", true)
|
||||
.withArgument("player", "Player name", OPTIONAL)
|
||||
.permission(AdminPermission.GET_EMAIL)
|
||||
.executableCommand(GetEmailCommand.class)
|
||||
.register();
|
||||
@ -270,8 +273,8 @@ public class CommandInitializer {
|
||||
.labels("setemail", "setmail", "chgemail", "chgmail")
|
||||
.description("Change player's email")
|
||||
.detailedDescription("Change the email address of the specified player.")
|
||||
.withArgument("player", "Player name", false)
|
||||
.withArgument("email", "Player email", false)
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.withArgument("email", "Player email", MANDATORY)
|
||||
.permission(AdminPermission.CHANGE_EMAIL)
|
||||
.executableCommand(SetEmailCommand.class)
|
||||
.register();
|
||||
@ -282,7 +285,7 @@ public class CommandInitializer {
|
||||
.labels("getip", "ip")
|
||||
.description("Get player's IP")
|
||||
.detailedDescription("Get the IP address of the specified online player.")
|
||||
.withArgument("player", "Player name", false)
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.permission(AdminPermission.GET_IP)
|
||||
.executableCommand(GetIpCommand.class)
|
||||
.register();
|
||||
@ -333,7 +336,7 @@ public class CommandInitializer {
|
||||
.labels("purge", "delete")
|
||||
.description("Purge old data")
|
||||
.detailedDescription("Purge old AuthMeReloaded data longer than the specified number of days ago.")
|
||||
.withArgument("days", "Number of days", false)
|
||||
.withArgument("days", "Number of days", MANDATORY)
|
||||
.permission(AdminPermission.PURGE)
|
||||
.executableCommand(PurgeCommand.class)
|
||||
.register();
|
||||
@ -344,8 +347,8 @@ public class CommandInitializer {
|
||||
.labels("purgeplayer")
|
||||
.description("Purges the data of one player")
|
||||
.detailedDescription("Purges data of the given player.")
|
||||
.withArgument("player", "The player to purge", false)
|
||||
.withArgument("options", "'force' to run without checking if player is registered", true)
|
||||
.withArgument("player", "The player to purge", MANDATORY)
|
||||
.withArgument("options", "'force' to run without checking if player is registered", OPTIONAL)
|
||||
.permission(AdminPermission.PURGE_PLAYER)
|
||||
.executableCommand(PurgePlayerCommand.class)
|
||||
.register();
|
||||
@ -367,7 +370,7 @@ public class CommandInitializer {
|
||||
"resetlastposition", "resetlastpos")
|
||||
.description("Purge player's last position")
|
||||
.detailedDescription("Purge the last know position of the specified player or all of them.")
|
||||
.withArgument("player/*", "Player name or * for all players", false)
|
||||
.withArgument("player/*", "Player name or * for all players", MANDATORY)
|
||||
.permission(AdminPermission.PURGE_LAST_POSITION)
|
||||
.executableCommand(PurgeLastPositionCommand.class)
|
||||
.register();
|
||||
@ -388,7 +391,7 @@ public class CommandInitializer {
|
||||
.labels("switchantibot", "toggleantibot", "antibot")
|
||||
.description("Switch AntiBot mode")
|
||||
.detailedDescription("Switch or toggle the AntiBot mode to the specified state.")
|
||||
.withArgument("mode", "ON / OFF", true)
|
||||
.withArgument("mode", "ON / OFF", OPTIONAL)
|
||||
.permission(AdminPermission.SWITCH_ANTIBOT)
|
||||
.executableCommand(SwitchAntiBotCommand.class)
|
||||
.register();
|
||||
@ -419,7 +422,7 @@ public class CommandInitializer {
|
||||
.description("Converter command")
|
||||
.detailedDescription("Converter command for AuthMeReloaded.")
|
||||
.withArgument("job", "Conversion job: xauth / crazylogin / rakamak / "
|
||||
+ "royalauth / vauth / sqliteToSql / mysqlToSqlite / loginsecurity", true)
|
||||
+ "royalauth / vauth / sqliteToSql / mysqlToSqlite / loginsecurity", OPTIONAL)
|
||||
.permission(AdminPermission.CONVERTER)
|
||||
.executableCommand(ConverterCommand.class)
|
||||
.register();
|
||||
@ -427,11 +430,10 @@ public class CommandInitializer {
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("messages", "msg")
|
||||
.description("Add missing messages")
|
||||
.detailedDescription("Adds missing messages to the current messages file.")
|
||||
.withArgument("help", "Add 'help' to update the help messages file", true)
|
||||
.description("Add missing help messages")
|
||||
.detailedDescription("Adds missing texts to the current help messages file.")
|
||||
.permission(AdminPermission.UPDATE_MESSAGES)
|
||||
.executableCommand(MessagesCommand.class)
|
||||
.executableCommand(UpdateHelpMessagesCommand.class)
|
||||
.register();
|
||||
|
||||
CommandDescription.builder()
|
||||
@ -448,9 +450,9 @@ public class CommandInitializer {
|
||||
.labels("debug", "dbg")
|
||||
.description("Debug features")
|
||||
.detailedDescription("Allows various operations for debugging.")
|
||||
.withArgument("child", "The child to execute", true)
|
||||
.withArgument("arg", "argument (depends on debug section)", true)
|
||||
.withArgument("arg", "argument (depends on debug section)", true)
|
||||
.withArgument("child", "The child to execute", OPTIONAL)
|
||||
.withArgument("arg", "argument (depends on debug section)", OPTIONAL)
|
||||
.withArgument("arg", "argument (depends on debug section)", OPTIONAL)
|
||||
.permission(DebugSectionPermissions.DEBUG_COMMAND)
|
||||
.executableCommand(DebugCommand.class)
|
||||
.register();
|
||||
@ -489,8 +491,8 @@ public class CommandInitializer {
|
||||
.labels("add", "addemail", "addmail")
|
||||
.description("Add Email")
|
||||
.detailedDescription("Add a new email address to your account.")
|
||||
.withArgument("email", "Email address", false)
|
||||
.withArgument("verifyEmail", "Email address verification", false)
|
||||
.withArgument("email", "Email address", MANDATORY)
|
||||
.withArgument("verifyEmail", "Email address verification", MANDATORY)
|
||||
.permission(PlayerPermission.ADD_EMAIL)
|
||||
.executableCommand(AddEmailCommand.class)
|
||||
.register();
|
||||
@ -501,8 +503,8 @@ public class CommandInitializer {
|
||||
.labels("change", "changeemail", "changemail")
|
||||
.description("Change Email")
|
||||
.detailedDescription("Change an email address of your account.")
|
||||
.withArgument("oldEmail", "Old email address", false)
|
||||
.withArgument("newEmail", "New email address", false)
|
||||
.withArgument("oldEmail", "Old email address", MANDATORY)
|
||||
.withArgument("newEmail", "New email address", MANDATORY)
|
||||
.permission(PlayerPermission.CHANGE_EMAIL)
|
||||
.executableCommand(ChangeEmailCommand.class)
|
||||
.register();
|
||||
@ -514,7 +516,7 @@ public class CommandInitializer {
|
||||
.description("Recover password using email")
|
||||
.detailedDescription("Recover your account using an Email address by sending a mail containing "
|
||||
+ "a new password.")
|
||||
.withArgument("email", "Email address", false)
|
||||
.withArgument("email", "Email address", MANDATORY)
|
||||
.permission(PlayerPermission.RECOVER_EMAIL)
|
||||
.executableCommand(RecoverEmailCommand.class)
|
||||
.register();
|
||||
@ -525,7 +527,7 @@ public class CommandInitializer {
|
||||
.labels("code")
|
||||
.description("Submit code to recover password")
|
||||
.detailedDescription("Recover your account by submitting a code delivered to your email.")
|
||||
.withArgument("code", "Recovery code", false)
|
||||
.withArgument("code", "Recovery code", MANDATORY)
|
||||
.permission(PlayerPermission.RECOVER_EMAIL)
|
||||
.executableCommand(ProcessCodeCommand.class)
|
||||
.register();
|
||||
@ -536,14 +538,74 @@ public class CommandInitializer {
|
||||
.labels("setpassword")
|
||||
.description("Set new password after recovery")
|
||||
.detailedDescription("Set a new password after successfully recovering your account.")
|
||||
.withArgument("password", "New password", false)
|
||||
.withArgument("password", "New password", MANDATORY)
|
||||
.permission(PlayerPermission.RECOVER_EMAIL)
|
||||
.executableCommand(SetPasswordCommand.class)
|
||||
.executableCommand(EmailSetPasswordCommand.class)
|
||||
.register();
|
||||
|
||||
return emailBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command description object for {@code /totp} including its children.
|
||||
*
|
||||
* @return the totp base command description
|
||||
*/
|
||||
private CommandDescription buildTotpBaseCommand() {
|
||||
// Register the base totp command
|
||||
CommandDescription totpBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("totp", "2fa")
|
||||
.description("TOTP commands")
|
||||
.detailedDescription("Performs actions related to two-factor authentication.")
|
||||
.executableCommand(TotpBaseCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base totp code
|
||||
CommandDescription.builder()
|
||||
.parent(totpBase)
|
||||
.labels("code", "c")
|
||||
.description("Command for logging in")
|
||||
.detailedDescription("Processes the two-factor authentication code during login.")
|
||||
.withArgument("code", "The TOTP code to use to log in", MANDATORY)
|
||||
.executableCommand(TotpCodeCommand.class)
|
||||
.register();
|
||||
|
||||
// Register totp add
|
||||
CommandDescription.builder()
|
||||
.parent(totpBase)
|
||||
.labels("add")
|
||||
.description("Enables TOTP")
|
||||
.detailedDescription("Enables two-factor authentication for your account.")
|
||||
.permission(PlayerPermission.ENABLE_TWO_FACTOR_AUTH)
|
||||
.executableCommand(AddTotpCommand.class)
|
||||
.register();
|
||||
|
||||
// Register totp confirm
|
||||
CommandDescription.builder()
|
||||
.parent(totpBase)
|
||||
.labels("confirm")
|
||||
.description("Enables TOTP after successful code")
|
||||
.detailedDescription("Saves the generated TOTP secret after confirmation.")
|
||||
.withArgument("code", "Code from the given secret from /totp add", MANDATORY)
|
||||
.permission(PlayerPermission.ENABLE_TWO_FACTOR_AUTH)
|
||||
.executableCommand(ConfirmTotpCommand.class)
|
||||
.register();
|
||||
|
||||
// Register totp remove
|
||||
CommandDescription.builder()
|
||||
.parent(totpBase)
|
||||
.labels("remove")
|
||||
.description("Removes TOTP")
|
||||
.detailedDescription("Disables two-factor authentication for your account.")
|
||||
.withArgument("code", "Current 2FA code", MANDATORY)
|
||||
.permission(PlayerPermission.DISABLE_TWO_FACTOR_AUTH)
|
||||
.executableCommand(RemoveTotpCommand.class)
|
||||
.register();
|
||||
|
||||
return totpBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the help command on all base commands, e.g. to register /authme help or /register help.
|
||||
*
|
||||
@ -558,7 +620,7 @@ public class CommandInitializer {
|
||||
.labels(helpCommandLabels)
|
||||
.description("View help")
|
||||
.detailedDescription("View detailed help for /" + base.getLabels().get(0) + " commands.")
|
||||
.withArgument("query", "The command or query to view help for.", true)
|
||||
.withArgument("query", "The command or query to view help for.", OPTIONAL)
|
||||
.executableCommand(HelpCommand.class)
|
||||
.register();
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.datasource.DataSourceResult;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -25,8 +25,8 @@ public class GetEmailCommand implements ExecutableCommand {
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
|
||||
|
||||
DataSourceResult<String> email = dataSource.getEmail(playerName);
|
||||
if (email.playerExists()) {
|
||||
DataSourceValue<String> email = dataSource.getEmail(playerName);
|
||||
if (email.rowExists()) {
|
||||
sender.sendMessage("[AuthMe] " + playerName + "'s email: " + email.getValue());
|
||||
} else {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
|
@ -1,79 +0,0 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.command.help.HelpMessagesService;
|
||||
import fr.xephi.authme.initialization.DataFolder;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.service.HelpTranslationGenerator;
|
||||
import fr.xephi.authme.service.MessageUpdater;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.PluginSettings;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Messages command, updates the user's messages file with any missing files
|
||||
* from the provided file in the JAR.
|
||||
*/
|
||||
public class MessagesCommand implements ExecutableCommand {
|
||||
|
||||
private static final String DEFAULT_LANGUAGE = "en";
|
||||
|
||||
@Inject
|
||||
private Settings settings;
|
||||
@Inject
|
||||
@DataFolder
|
||||
private File dataFolder;
|
||||
@Inject
|
||||
private Messages messages;
|
||||
@Inject
|
||||
private HelpTranslationGenerator helpTranslationGenerator;
|
||||
@Inject
|
||||
private HelpMessagesService helpMessagesService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
if (!arguments.isEmpty() && "help".equalsIgnoreCase(arguments.get(0))) {
|
||||
updateHelpFile(sender);
|
||||
} else {
|
||||
updateMessagesFile(sender);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateHelpFile(CommandSender sender) {
|
||||
try {
|
||||
helpTranslationGenerator.updateHelpFile();
|
||||
sender.sendMessage("Successfully updated the help file");
|
||||
helpMessagesService.reload();
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage("Could not update help file: " + e.getMessage());
|
||||
ConsoleLogger.logException("Could not update help file:", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMessagesFile(CommandSender sender) {
|
||||
final String language = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE);
|
||||
try {
|
||||
boolean isFileUpdated = new MessageUpdater(
|
||||
new File(dataFolder, getMessagePath(language)),
|
||||
getMessagePath(language),
|
||||
getMessagePath(DEFAULT_LANGUAGE))
|
||||
.executeCopy(sender);
|
||||
if (isFileUpdated) {
|
||||
messages.reload();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage("Could not update messages: " + e.getMessage());
|
||||
ConsoleLogger.logException("Could not update messages:", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getMessagePath(String code) {
|
||||
return "messages/messages_" + code + ".yml";
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.task.purge.PurgeService;
|
||||
import org.bukkit.ChatColor;
|
||||
@ -26,10 +27,8 @@ public class PurgeCommand implements ExecutableCommand {
|
||||
String daysStr = arguments.get(0);
|
||||
|
||||
// Convert the days string to an integer value, and make sure it's valid
|
||||
int days;
|
||||
try {
|
||||
days = Integer.parseInt(daysStr);
|
||||
} catch (NumberFormatException ex) {
|
||||
Integer days = Ints.tryParse(daysStr);
|
||||
if (days == null) {
|
||||
sender.sendMessage(ChatColor.RED + "The value you've entered is invalid!");
|
||||
return;
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ public class RegisterAdminCommand implements ExecutableCommand {
|
||||
final Player player = bukkitService.getPlayerExact(playerName);
|
||||
if (player != null) {
|
||||
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() ->
|
||||
player.kickPlayer(commonService.retrieveSingleMessage(MessageKey.KICK_FOR_ADMIN_REGISTER)));
|
||||
player.kickPlayer(commonService.retrieveSingleMessage(player, MessageKey.KICK_FOR_ADMIN_REGISTER)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.command.help.HelpMessagesService;
|
||||
import fr.xephi.authme.service.HelpTranslationGenerator;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Messages command, updates the user's help messages file with any missing files
|
||||
* from the provided file in the JAR.
|
||||
*/
|
||||
public class UpdateHelpMessagesCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private HelpTranslationGenerator helpTranslationGenerator;
|
||||
@Inject
|
||||
private HelpMessagesService helpMessagesService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
try {
|
||||
File updatedFile = helpTranslationGenerator.updateHelpFile();
|
||||
sender.sendMessage("Successfully updated the help file '" + updatedFile.getName() + "'");
|
||||
helpMessagesService.reloadMessagesFile();
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage("Could not update help file: " + e.getMessage());
|
||||
ConsoleLogger.logException("Could not update help file:", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -172,6 +172,11 @@ class MySqlDefaultChanger implements DebugSection {
|
||||
+ sender.getName() + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the current definitions of all {@link Columns} which can be migrated.
|
||||
*
|
||||
* @param sender command sender to output the data to
|
||||
*/
|
||||
private void showColumnDetails(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.BLUE + "MySQL column details");
|
||||
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
|
@ -77,6 +77,7 @@ class PlayerAuthViewer implements DebugSection {
|
||||
HashedPassword hashedPass = auth.getPassword();
|
||||
sender.sendMessage("Hash / salt (partial): '" + safeSubstring(hashedPass.getHash(), 6)
|
||||
+ "' / '" + safeSubstring(hashedPass.getSalt(), 4) + "'");
|
||||
sender.sendMessage("TOTP code (partial): '" + safeSubstring(auth.getTotpKey(), 3) + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,8 +1,8 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.datasource.DataSourceResult;
|
||||
import fr.xephi.authme.mail.SendMailSsl;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
@ -82,8 +82,8 @@ class TestEmailSender implements DebugSection {
|
||||
*/
|
||||
private String getEmail(CommandSender sender, List<String> arguments) {
|
||||
if (arguments.isEmpty()) {
|
||||
DataSourceResult<String> emailResult = dataSource.getEmail(sender.getName());
|
||||
if (!emailResult.playerExists()) {
|
||||
DataSourceValue<String> emailResult = dataSource.getEmail(sender.getName());
|
||||
if (!emailResult.rowExists()) {
|
||||
sender.sendMessage(ChatColor.RED + "Please provide an email address, "
|
||||
+ "e.g. /authme debug mail test@example.com");
|
||||
return null;
|
||||
|
@ -4,6 +4,7 @@ import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.data.captcha.LoginCaptchaManager;
|
||||
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
|
||||
import fr.xephi.authme.data.limbo.LimboMessageType;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
@ -80,6 +81,6 @@ public class CaptchaCommand extends PlayerCommand {
|
||||
String newCode = registrationCaptchaManager.getCaptchaCodeOrGenerateNew(player.getName());
|
||||
commonService.send(player, MessageKey.CAPTCHA_WRONG_ERROR, newCode);
|
||||
}
|
||||
limboService.resetMessageTask(player, false);
|
||||
limboService.resetMessageTask(player, LimboMessageType.REGISTER);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import java.util.List;
|
||||
/**
|
||||
* Command for changing password following successful recovery.
|
||||
*/
|
||||
public class SetPasswordCommand extends PlayerCommand {
|
||||
public class EmailSetPasswordCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
@ -45,11 +45,14 @@ public class SetPasswordCommand extends PlayerCommand {
|
||||
if (!result.hasError()) {
|
||||
HashedPassword hashedPassword = passwordSecurity.computeHash(password, name);
|
||||
dataSource.updatePassword(name, hashedPassword);
|
||||
recoveryService.removeFromSuccessfulRecovery(player);
|
||||
ConsoleLogger.info("Player '" + name + "' has changed their password from recovery");
|
||||
commonService.send(player, MessageKey.PASSWORD_CHANGED_SUCCESS);
|
||||
} else {
|
||||
commonService.send(player, result.getMessageKey(), result.getArgs());
|
||||
}
|
||||
} else {
|
||||
commonService.send(player, MessageKey.CHANGE_PASSWORD_EXPIRED);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.datasource.DataSourceResult;
|
||||
import fr.xephi.authme.mail.EmailService;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
@ -58,8 +58,8 @@ public class RecoverEmailCommand extends PlayerCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
DataSourceResult<String> emailResult = dataSource.getEmail(playerName);
|
||||
if (!emailResult.playerExists()) {
|
||||
DataSourceValue<String> emailResult = dataSource.getEmail(playerName);
|
||||
if (!emailResult.rowExists()) {
|
||||
commonService.send(player, MessageKey.USAGE_REGISTER);
|
||||
return;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ public class LoginCommand extends PlayerCommand {
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
final String password = arguments.get(0);
|
||||
String password = arguments.get(0);
|
||||
management.performLogin(player, password);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,43 @@
|
||||
package fr.xephi.authme.command.executable.totp;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.security.totp.GenerateTotpService;
|
||||
import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for a player to enable TOTP.
|
||||
*/
|
||||
public class AddTotpCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private GenerateTotpService generateTotpService;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
PlayerAuth auth = playerCache.getAuth(player.getName());
|
||||
if (auth == null) {
|
||||
messages.send(player, MessageKey.NOT_LOGGED_IN);
|
||||
} else if (auth.getTotpKey() == null) {
|
||||
TotpGenerationResult createdTotpInfo = generateTotpService.generateTotpKey(player);
|
||||
messages.send(player, MessageKey.TWO_FACTOR_CREATE,
|
||||
createdTotpInfo.getTotpKey(), createdTotpInfo.getAuthenticatorQrCodeUrl());
|
||||
messages.send(player, MessageKey.TWO_FACTOR_CREATE_CONFIRMATION_REQUIRED);
|
||||
} else {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package fr.xephi.authme.command.executable.totp;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.security.totp.GenerateTotpService;
|
||||
import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command to enable TOTP by supplying the proper code as confirmation.
|
||||
*/
|
||||
public class ConfirmTotpCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private GenerateTotpService generateTotpService;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
PlayerAuth auth = playerCache.getAuth(player.getName());
|
||||
if (auth == null) {
|
||||
messages.send(player, MessageKey.NOT_LOGGED_IN);
|
||||
} else if (auth.getTotpKey() != null) {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED);
|
||||
} else {
|
||||
verifyTotpCodeConfirmation(player, auth, arguments.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyTotpCodeConfirmation(Player player, PlayerAuth auth, String inputTotpCode) {
|
||||
final TotpGenerationResult totpDetails = generateTotpService.getGeneratedTotpKey(player);
|
||||
if (totpDetails == null) {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_NO_CODE);
|
||||
} else {
|
||||
boolean isCodeValid = generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, inputTotpCode);
|
||||
if (isCodeValid) {
|
||||
generateTotpService.removeGenerateTotpKey(player);
|
||||
insertTotpKeyIntoDatabase(player, auth, totpDetails);
|
||||
} else {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_WRONG_CODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void insertTotpKeyIntoDatabase(Player player, PlayerAuth auth, TotpGenerationResult totpDetails) {
|
||||
if (dataSource.setTotpKey(player.getName(), totpDetails.getTotpKey())) {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ENABLE_SUCCESS);
|
||||
auth.setTotpKey(totpDetails.getTotpKey());
|
||||
playerCache.updatePlayer(auth);
|
||||
ConsoleLogger.info("Player '" + player.getName() + "' has successfully added a TOTP key to their account");
|
||||
} else {
|
||||
messages.send(player, MessageKey.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package fr.xephi.authme.command.executable.totp;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.security.totp.TotpAuthenticator;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for a player to remove 2FA authentication.
|
||||
*/
|
||||
public class RemoveTotpCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private TotpAuthenticator totpAuthenticator;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
PlayerAuth auth = playerCache.getAuth(player.getName());
|
||||
if (auth == null) {
|
||||
messages.send(player, MessageKey.NOT_LOGGED_IN);
|
||||
} else if (auth.getTotpKey() == null) {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_NOT_ENABLED_ERROR);
|
||||
} else {
|
||||
if (totpAuthenticator.checkCode(auth, arguments.get(0))) {
|
||||
removeTotpKeyFromDatabase(player, auth);
|
||||
} else {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_INVALID_CODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeTotpKeyFromDatabase(Player player, PlayerAuth auth) {
|
||||
if (dataSource.removeTotpKey(auth.getNickname())) {
|
||||
auth.setTotpKey(null);
|
||||
playerCache.updatePlayer(auth);
|
||||
messages.send(player, MessageKey.TWO_FACTOR_REMOVED_SUCCESS);
|
||||
ConsoleLogger.info("Player '" + player.getName() + "' removed their TOTP key");
|
||||
} else {
|
||||
messages.send(player, MessageKey.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package fr.xephi.authme.command.executable.totp;
|
||||
|
||||
import fr.xephi.authme.command.CommandMapper;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.command.FoundCommandResult;
|
||||
import fr.xephi.authme.command.help.HelpProvider;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Base command for /totp.
|
||||
*/
|
||||
public class TotpBaseCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private CommandMapper commandMapper;
|
||||
|
||||
@Inject
|
||||
private HelpProvider helpProvider;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
FoundCommandResult result = commandMapper.mapPartsToCommand(sender, Collections.singletonList("totp"));
|
||||
helpProvider.outputHelp(sender, result, HelpProvider.SHOW_CHILDREN);
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package fr.xephi.authme.command.executable.totp;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.data.limbo.LimboPlayer;
|
||||
import fr.xephi.authme.data.limbo.LimboPlayerState;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.process.login.AsynchronousLogin;
|
||||
import fr.xephi.authme.security.totp.TotpAuthenticator;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TOTP code command for processing the 2FA code during the login process.
|
||||
*/
|
||||
public class TotpCodeCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private LimboService limboService;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Inject
|
||||
private TotpAuthenticator totpAuthenticator;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private AsynchronousLogin asynchronousLogin;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
if (playerCache.isAuthenticated(player.getName())) {
|
||||
messages.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerAuth auth = dataSource.getAuth(player.getName());
|
||||
if (auth == null) {
|
||||
messages.send(player, MessageKey.REGISTER_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
LimboPlayer limbo = limboService.getLimboPlayer(player.getName());
|
||||
if (limbo != null && limbo.getState() == LimboPlayerState.TOTP_REQUIRED) {
|
||||
processCode(player, auth, arguments.get(0));
|
||||
} else {
|
||||
ConsoleLogger.debug(() -> "Aborting TOTP check for player '" + player.getName()
|
||||
+ "'. Invalid limbo state: " + (limbo == null ? "no limbo" : limbo.getState()));
|
||||
messages.send(player, MessageKey.LOGIN_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private void processCode(Player player, PlayerAuth auth, String inputCode) {
|
||||
boolean isCodeValid = totpAuthenticator.checkCode(auth, inputCode);
|
||||
if (isCodeValid) {
|
||||
ConsoleLogger.debug("Successfully checked TOTP code for `{0}`", player.getName());
|
||||
asynchronousLogin.performLogin(player, auth);
|
||||
} else {
|
||||
ConsoleLogger.debug("Input TOTP code was invalid for player `{0}`", player.getName());
|
||||
messages.send(player, MessageKey.TWO_FACTOR_INVALID_CODE);
|
||||
}
|
||||
}
|
||||
}
|
@ -4,9 +4,7 @@ import com.google.common.base.CaseFormat;
|
||||
import fr.xephi.authme.command.CommandArgumentDescription;
|
||||
import fr.xephi.authme.command.CommandDescription;
|
||||
import fr.xephi.authme.command.CommandUtils;
|
||||
import fr.xephi.authme.initialization.Reloadable;
|
||||
import fr.xephi.authme.message.MessageFileHandlerProvider;
|
||||
import fr.xephi.authme.message.MessageFileHandler;
|
||||
import fr.xephi.authme.message.HelpMessagesFileHandler;
|
||||
import fr.xephi.authme.permission.DefaultPermission;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@ -16,20 +14,18 @@ import java.util.stream.Collectors;
|
||||
/**
|
||||
* Manages translatable help messages.
|
||||
*/
|
||||
public class HelpMessagesService implements Reloadable {
|
||||
public class HelpMessagesService {
|
||||
|
||||
private static final String COMMAND_PREFIX = "commands.";
|
||||
private static final String DESCRIPTION_SUFFIX = ".description";
|
||||
private static final String DETAILED_DESCRIPTION_SUFFIX = ".detailedDescription";
|
||||
private static final String DEFAULT_PERMISSIONS_PATH = "common.defaultPermissions.";
|
||||
|
||||
private final MessageFileHandlerProvider messageFileHandlerProvider;
|
||||
private MessageFileHandler messageFileHandler;
|
||||
private final HelpMessagesFileHandler helpMessagesFileHandler;
|
||||
|
||||
@Inject
|
||||
HelpMessagesService(MessageFileHandlerProvider messageFileHandlerProvider) {
|
||||
this.messageFileHandlerProvider = messageFileHandlerProvider;
|
||||
reload();
|
||||
HelpMessagesService(HelpMessagesFileHandler helpMessagesFileHandler) {
|
||||
this.helpMessagesFileHandler = helpMessagesFileHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,7 +36,7 @@ public class HelpMessagesService implements Reloadable {
|
||||
*/
|
||||
public CommandDescription buildLocalizedDescription(CommandDescription command) {
|
||||
final String path = COMMAND_PREFIX + getCommandSubPath(command);
|
||||
if (!messageFileHandler.hasSection(path)) {
|
||||
if (!helpMessagesFileHandler.hasSection(path)) {
|
||||
// Messages file does not have a section for this command - return the provided command
|
||||
return command;
|
||||
}
|
||||
@ -72,36 +68,39 @@ public class HelpMessagesService implements Reloadable {
|
||||
}
|
||||
|
||||
public String getMessage(HelpMessage message) {
|
||||
return messageFileHandler.getMessage(message.getKey());
|
||||
return helpMessagesFileHandler.getMessage(message.getKey());
|
||||
}
|
||||
|
||||
public String getMessage(HelpSection section) {
|
||||
return messageFileHandler.getMessage(section.getKey());
|
||||
return helpMessagesFileHandler.getMessage(section.getKey());
|
||||
}
|
||||
|
||||
public String getMessage(DefaultPermission defaultPermission) {
|
||||
// e.g. {default_permissions_path}.opOnly for DefaultPermission.OP_ONLY
|
||||
String path = DEFAULT_PERMISSIONS_PATH + getDefaultPermissionsSubPath(defaultPermission);
|
||||
return messageFileHandler.getMessage(path);
|
||||
return helpMessagesFileHandler.getMessage(path);
|
||||
}
|
||||
|
||||
public static String getDefaultPermissionsSubPath(DefaultPermission defaultPermission) {
|
||||
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, defaultPermission.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
messageFileHandler = messageFileHandlerProvider.initializeHandler(
|
||||
lang -> "messages/help_" + lang + ".yml");
|
||||
}
|
||||
|
||||
private String getText(String path, Supplier<String> defaultTextGetter) {
|
||||
String message = messageFileHandler.getMessageIfExists(path);
|
||||
String message = helpMessagesFileHandler.getMessageIfExists(path);
|
||||
return message == null
|
||||
? defaultTextGetter.get()
|
||||
: message;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Triggers a reload of the help messages file. Note that this method is not needed
|
||||
* to be called for /authme reload.
|
||||
*/
|
||||
public void reloadMessagesFile() {
|
||||
helpMessagesFileHandler.reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command subpath for the given command (i.e. the path to the translations for the given
|
||||
* command under "commands").
|
||||
|
@ -0,0 +1,75 @@
|
||||
package fr.xephi.authme.data;
|
||||
|
||||
import fr.xephi.authme.initialization.HasCleanup;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.permission.PlayerPermission;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.ProtectionSettings;
|
||||
import fr.xephi.authme.util.expiring.ExpiringSet;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class QuickCommandsProtectionManager implements SettingsDependent, HasCleanup {
|
||||
|
||||
private final PermissionsManager permissionsManager;
|
||||
|
||||
private final ExpiringSet<String> latestJoin;
|
||||
|
||||
@Inject
|
||||
public QuickCommandsProtectionManager(Settings settings, PermissionsManager permissionsManager) {
|
||||
this.permissionsManager = permissionsManager;
|
||||
long countTimeout = settings.getProperty(ProtectionSettings.QUICK_COMMANDS_DENIED_BEFORE_MILLISECONDS);
|
||||
latestJoin = new ExpiringSet<>(countTimeout, TimeUnit.MILLISECONDS);
|
||||
reload(settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the player in the set
|
||||
* @param name the player's name
|
||||
*/
|
||||
private void setJoin(String name) {
|
||||
latestJoin.add(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given player has the permission and should be saved in the set
|
||||
* @param player the player to check
|
||||
* @return true if the player has the permission, false otherwise
|
||||
*/
|
||||
private boolean shouldSavePlayer(Player player) {
|
||||
return permissionsManager.hasPermission(player, PlayerPermission.QUICK_COMMANDS_PROTECTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the player join
|
||||
* @param player the player to process
|
||||
*/
|
||||
public void processJoin(Player player) {
|
||||
if(shouldSavePlayer(player)) {
|
||||
setJoin(player.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given player is able to perform the command
|
||||
* @param name the name of the player to check
|
||||
* @return true if the player is not in the set (so it's allowed to perform the command), false otherwise
|
||||
*/
|
||||
public boolean isAllowed(String name) {
|
||||
return !latestJoin.contains(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(Settings settings) {
|
||||
long countTimeout = settings.getProperty(ProtectionSettings.QUICK_COMMANDS_DENIED_BEFORE_MILLISECONDS);
|
||||
latestJoin.setExpiration(countTimeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performCleanup() {
|
||||
latestJoin.removeExpiredEntries();
|
||||
}
|
||||
}
|
@ -97,7 +97,7 @@ public class TempbanManager implements SettingsDependent, HasCleanup {
|
||||
if (isEnabled) {
|
||||
final String name = player.getName();
|
||||
final String ip = PlayerUtils.getPlayerIp(player);
|
||||
final String reason = messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS);
|
||||
final String reason = messages.retrieveSingle(player, MessageKey.TEMPBAN_MAX_LOGINS);
|
||||
|
||||
final Date expires = new Date();
|
||||
long newTime = expires.getTime() + (length * MILLIS_PER_MINUTE);
|
||||
|
@ -1,7 +1,7 @@
|
||||
package fr.xephi.authme.data;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.datasource.DataSourceResult;
|
||||
import fr.xephi.authme.initialization.HasCleanup;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.mail.EmailService;
|
||||
@ -103,8 +103,8 @@ public class VerificationCodeManager implements SettingsDependent, HasCleanup {
|
||||
*/
|
||||
public boolean hasEmail(String name) {
|
||||
boolean result = false;
|
||||
DataSourceResult<String> emailResult = dataSource.getEmail(name);
|
||||
if (emailResult.playerExists()) {
|
||||
DataSourceValue<String> emailResult = dataSource.getEmail(name);
|
||||
if (emailResult.rowExists()) {
|
||||
final String email = emailResult.getValue();
|
||||
if (!Utils.isEmailEmpty(email)) {
|
||||
result = true;
|
||||
@ -130,8 +130,8 @@ public class VerificationCodeManager implements SettingsDependent, HasCleanup {
|
||||
* @param name the name of the player to generate a code for
|
||||
*/
|
||||
private void generateCode(String name) {
|
||||
DataSourceResult<String> emailResult = dataSource.getEmail(name);
|
||||
if (emailResult.playerExists()) {
|
||||
DataSourceValue<String> emailResult = dataSource.getEmail(name);
|
||||
if (emailResult.rowExists()) {
|
||||
final String email = emailResult.getValue();
|
||||
if (!Utils.isEmailEmpty(email)) {
|
||||
String code = RandomStringUtils.generateNum(6); // 6 digits code
|
||||
@ -162,7 +162,7 @@ public class VerificationCodeManager implements SettingsDependent, HasCleanup {
|
||||
*
|
||||
* @param name the name of the player to generate a code for
|
||||
*/
|
||||
public void verify(String name){
|
||||
public void verify(String name) {
|
||||
verifiedPlayers.add(name.toLowerCase());
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ public class PlayerAuth {
|
||||
/** The player's name in the correct casing, e.g. "Xephi". */
|
||||
private String realName;
|
||||
private HashedPassword password;
|
||||
private String totpKey;
|
||||
private String email;
|
||||
private String lastIp;
|
||||
private int groupId;
|
||||
@ -160,6 +161,14 @@ public class PlayerAuth {
|
||||
this.registrationDate = registrationDate;
|
||||
}
|
||||
|
||||
public String getTotpKey() {
|
||||
return totpKey;
|
||||
}
|
||||
|
||||
public void setTotpKey(String totpKey) {
|
||||
this.totpKey = totpKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof PlayerAuth)) {
|
||||
@ -195,6 +204,7 @@ public class PlayerAuth {
|
||||
private String name;
|
||||
private String realName;
|
||||
private HashedPassword password;
|
||||
private String totpKey;
|
||||
private String lastIp;
|
||||
private String email;
|
||||
private int groupId = -1;
|
||||
@ -219,6 +229,7 @@ public class PlayerAuth {
|
||||
auth.nickname = checkNotNull(name).toLowerCase();
|
||||
auth.realName = firstNonNull(realName, "Player");
|
||||
auth.password = firstNonNull(password, new HashedPassword(""));
|
||||
auth.totpKey = totpKey;
|
||||
auth.email = DB_EMAIL_DEFAULT.equals(email) ? null : email;
|
||||
auth.lastIp = lastIp; // Don't check against default value 127.0.0.1 as it may be a legit value
|
||||
auth.groupId = groupId;
|
||||
@ -258,6 +269,11 @@ public class PlayerAuth {
|
||||
return password(new HashedPassword(hash, salt));
|
||||
}
|
||||
|
||||
public Builder totpKey(String totpKey) {
|
||||
this.totpKey = totpKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder lastIp(String lastIp) {
|
||||
this.lastIp = lastIp;
|
||||
return this;
|
||||
|
@ -32,7 +32,7 @@ public enum AllowFlightRestoreType {
|
||||
}
|
||||
},
|
||||
|
||||
/** Always set flight enabled to false. */
|
||||
/** The user's flight handling is not modified. */
|
||||
NOTHING {
|
||||
@Override
|
||||
public void restoreAllowFlight(Player player, LimboPlayer limbo) {
|
||||
|
@ -0,0 +1,11 @@
|
||||
package fr.xephi.authme.data.limbo;
|
||||
|
||||
public enum LimboMessageType {
|
||||
|
||||
REGISTER,
|
||||
|
||||
LOG_IN,
|
||||
|
||||
TOTP_CODE
|
||||
|
||||
}
|
@ -23,6 +23,7 @@ public class LimboPlayer {
|
||||
private final float flySpeed;
|
||||
private BukkitTask timeoutTask = null;
|
||||
private MessageTask messageTask = null;
|
||||
private LimboPlayerState state = LimboPlayerState.PASSWORD_REQUIRED;
|
||||
|
||||
public LimboPlayer(Location loc, boolean operator, Collection<String> groups, boolean fly, float walkSpeed,
|
||||
float flySpeed) {
|
||||
@ -124,4 +125,12 @@ public class LimboPlayer {
|
||||
setMessageTask(null);
|
||||
setTimeoutTask(null);
|
||||
}
|
||||
|
||||
public LimboPlayerState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(LimboPlayerState state) {
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
package fr.xephi.authme.data.limbo;
|
||||
|
||||
public enum LimboPlayerState {
|
||||
|
||||
PASSWORD_REQUIRED,
|
||||
|
||||
TOTP_REQUIRED
|
||||
|
||||
}
|
@ -45,13 +45,13 @@ class LimboPlayerTaskManager {
|
||||
*
|
||||
* @param player the player
|
||||
* @param limbo the associated limbo player of the player
|
||||
* @param isRegistered whether the player is registered or not (needed to determine the message in the task)
|
||||
* @param messageType message type
|
||||
*/
|
||||
void registerMessageTask(Player player, LimboPlayer limbo, boolean isRegistered) {
|
||||
void registerMessageTask(Player player, LimboPlayer limbo, LimboMessageType messageType) {
|
||||
int interval = settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL);
|
||||
MessageResult messageResult = getMessageKey(player.getName(), isRegistered);
|
||||
MessageResult result = getMessageKey(player.getName(), messageType);
|
||||
if (interval > 0) {
|
||||
String[] joinMessage = messages.retrieveSingle(messageResult.messageKey, messageResult.args).split("\n");
|
||||
String[] joinMessage = messages.retrieveSingle(player, result.messageKey, result.args).split("\n");
|
||||
MessageTask messageTask = new MessageTask(player, joinMessage);
|
||||
bukkitService.runTaskTimer(messageTask, 2 * TICKS_PER_SECOND, interval * TICKS_PER_SECOND);
|
||||
limbo.setMessageTask(messageTask);
|
||||
@ -67,7 +67,7 @@ class LimboPlayerTaskManager {
|
||||
void registerTimeoutTask(Player player, LimboPlayer limbo) {
|
||||
final int timeout = settings.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND;
|
||||
if (timeout > 0) {
|
||||
String message = messages.retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR);
|
||||
String message = messages.retrieveSingle(player, MessageKey.LOGIN_TIMEOUT_ERROR);
|
||||
BukkitTask task = bukkitService.runTaskLater(new TimeoutTask(player, message, playerCache), timeout);
|
||||
limbo.setTimeoutTask(task);
|
||||
}
|
||||
@ -89,12 +89,14 @@ class LimboPlayerTaskManager {
|
||||
* Returns the appropriate message key according to the registration status and settings.
|
||||
*
|
||||
* @param name the player's name
|
||||
* @param isRegistered whether or not the username is registered
|
||||
* @param messageType the message to show
|
||||
* @return the message key to display to the user
|
||||
*/
|
||||
private MessageResult getMessageKey(String name, boolean isRegistered) {
|
||||
if (isRegistered) {
|
||||
private MessageResult getMessageKey(String name, LimboMessageType messageType) {
|
||||
if (messageType == LimboMessageType.LOG_IN) {
|
||||
return new MessageResult(MessageKey.LOGIN_MESSAGE);
|
||||
} else if (messageType == LimboMessageType.TOTP_CODE) {
|
||||
return new MessageResult(MessageKey.TWO_FACTOR_CODE_REQUIRED);
|
||||
} else if (registrationCaptchaManager.isCaptchaRequired(name)) {
|
||||
final String captchaCode = registrationCaptchaManager.getCaptchaCodeOrGenerateNew(name);
|
||||
return new MessageResult(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, captchaCode);
|
||||
|
@ -69,7 +69,8 @@ public class LimboService {
|
||||
LimboPlayer limboPlayer = helper.merge(existingLimbo, limboFromDisk);
|
||||
limboPlayer = helper.merge(helper.createLimboPlayer(player, isRegistered, location), limboPlayer);
|
||||
|
||||
taskManager.registerMessageTask(player, limboPlayer, isRegistered);
|
||||
taskManager.registerMessageTask(player, limboPlayer,
|
||||
isRegistered ? LimboMessageType.LOG_IN : LimboMessageType.REGISTER);
|
||||
taskManager.registerTimeoutTask(player, limboPlayer);
|
||||
helper.revokeLimboStates(player);
|
||||
authGroupHandler.setGroup(player, limboPlayer,
|
||||
@ -134,7 +135,7 @@ public class LimboService {
|
||||
Optional<LimboPlayer> limboPlayer = getLimboOrLogError(player, "reset tasks");
|
||||
limboPlayer.ifPresent(limbo -> {
|
||||
taskManager.registerTimeoutTask(player, limbo);
|
||||
taskManager.registerMessageTask(player, limbo, true);
|
||||
taskManager.registerMessageTask(player, limbo, LimboMessageType.LOG_IN);
|
||||
});
|
||||
authGroupHandler.setGroup(player, limboPlayer.orElse(null), AuthGroupType.REGISTERED_UNAUTHENTICATED);
|
||||
}
|
||||
@ -143,11 +144,11 @@ public class LimboService {
|
||||
* Resets the message task associated with the player's LimboPlayer.
|
||||
*
|
||||
* @param player the player to set a new message task for
|
||||
* @param isRegistered whether or not the player is registered
|
||||
* @param messageType the message to show for the limbo player
|
||||
*/
|
||||
public void resetMessageTask(Player player, boolean isRegistered) {
|
||||
public void resetMessageTask(Player player, LimboMessageType messageType) {
|
||||
getLimboOrLogError(player, "reset message task")
|
||||
.ifPresent(limbo -> taskManager.registerMessageTask(player, limbo, isRegistered));
|
||||
.ifPresent(limbo -> taskManager.registerMessageTask(player, limbo, messageType));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3,7 +3,6 @@ package fr.xephi.authme.data.limbo;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.SpawnLoader;
|
||||
import fr.xephi.authme.settings.properties.LimboSettings;
|
||||
import fr.xephi.authme.settings.properties.RestrictionSettings;
|
||||
import org.bukkit.Location;
|
||||
@ -20,9 +19,6 @@ import static fr.xephi.authme.util.Utils.isCollectionEmpty;
|
||||
*/
|
||||
class LimboServiceHelper {
|
||||
|
||||
@Inject
|
||||
private SpawnLoader spawnLoader;
|
||||
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
|
||||
|
@ -0,0 +1,169 @@
|
||||
package fr.xephi.authme.datasource;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValueImpl;
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValues;
|
||||
import ch.jalu.datasourcecolumns.predicate.AlwaysTruePredicate;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.columnshandler.AuthMeColumns;
|
||||
import fr.xephi.authme.datasource.columnshandler.AuthMeColumnsHandler;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static ch.jalu.datasourcecolumns.data.UpdateValues.with;
|
||||
import static ch.jalu.datasourcecolumns.predicate.StandardPredicates.eq;
|
||||
import static ch.jalu.datasourcecolumns.predicate.StandardPredicates.eqIgnoreCase;
|
||||
import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException;
|
||||
|
||||
/**
|
||||
* Common type for SQL-based data sources. Classes implementing this
|
||||
* must ensure that {@link #columnsHandler} is initialized on creation.
|
||||
*/
|
||||
public abstract class AbstractSqlDataSource implements DataSource {
|
||||
|
||||
protected AuthMeColumnsHandler columnsHandler;
|
||||
|
||||
@Override
|
||||
public boolean isAuthAvailable(String user) {
|
||||
try {
|
||||
return columnsHandler.retrieve(user, AuthMeColumns.NAME).rowExists();
|
||||
} catch (SQLException e) {
|
||||
logSqlException(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashedPassword getPassword(String user) {
|
||||
try {
|
||||
DataSourceValues values = columnsHandler.retrieve(user, AuthMeColumns.PASSWORD, AuthMeColumns.SALT);
|
||||
if (values.rowExists()) {
|
||||
return new HashedPassword(values.get(AuthMeColumns.PASSWORD), values.get(AuthMeColumns.SALT));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logSqlException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveAuth(PlayerAuth auth) {
|
||||
return columnsHandler.insert(auth,
|
||||
AuthMeColumns.NAME, AuthMeColumns.NICK_NAME, AuthMeColumns.PASSWORD, AuthMeColumns.SALT,
|
||||
AuthMeColumns.EMAIL, AuthMeColumns.REGISTRATION_DATE, AuthMeColumns.REGISTRATION_IP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSession(String user) {
|
||||
try {
|
||||
DataSourceValue<Integer> result = columnsHandler.retrieve(user, AuthMeColumns.HAS_SESSION);
|
||||
return result.rowExists() && Integer.valueOf(1).equals(result.getValue());
|
||||
} catch (SQLException e) {
|
||||
logSqlException(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateSession(PlayerAuth auth) {
|
||||
return columnsHandler.update(auth, AuthMeColumns.LAST_IP, AuthMeColumns.LAST_LOGIN, AuthMeColumns.NICK_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updatePassword(PlayerAuth auth) {
|
||||
return updatePassword(auth.getNickname(), auth.getPassword());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updatePassword(String user, HashedPassword password) {
|
||||
return columnsHandler.update(user,
|
||||
with(AuthMeColumns.PASSWORD, password.getHash())
|
||||
.and(AuthMeColumns.SALT, password.getSalt()).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateQuitLoc(PlayerAuth auth) {
|
||||
return columnsHandler.update(auth,
|
||||
AuthMeColumns.LOCATION_X, AuthMeColumns.LOCATION_Y, AuthMeColumns.LOCATION_Z,
|
||||
AuthMeColumns.LOCATION_WORLD, AuthMeColumns.LOCATION_YAW, AuthMeColumns.LOCATION_PITCH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllAuthsByIp(String ip) {
|
||||
try {
|
||||
return columnsHandler.retrieve(eq(AuthMeColumns.LAST_IP, ip), AuthMeColumns.NAME);
|
||||
} catch (SQLException e) {
|
||||
logSqlException(e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countAuthsByEmail(String email) {
|
||||
return columnsHandler.count(eqIgnoreCase(AuthMeColumns.EMAIL, email));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateEmail(PlayerAuth auth) {
|
||||
return columnsHandler.update(auth, AuthMeColumns.EMAIL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogged(String user) {
|
||||
try {
|
||||
DataSourceValue<Integer> result = columnsHandler.retrieve(user, AuthMeColumns.IS_LOGGED);
|
||||
return result.rowExists() && Integer.valueOf(1).equals(result.getValue());
|
||||
} catch (SQLException e) {
|
||||
logSqlException(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogged(String user) {
|
||||
columnsHandler.update(user, AuthMeColumns.IS_LOGGED, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUnlogged(String user) {
|
||||
columnsHandler.update(user, AuthMeColumns.IS_LOGGED, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantSession(String user) {
|
||||
columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeSession(String user) {
|
||||
columnsHandler.update(user, AuthMeColumns.HAS_SESSION, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void purgeLogged() {
|
||||
columnsHandler.update(eq(AuthMeColumns.IS_LOGGED, 1), AuthMeColumns.IS_LOGGED, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccountsRegistered() {
|
||||
return columnsHandler.count(new AlwaysTruePredicate<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateRealName(String user, String realName) {
|
||||
return columnsHandler.update(user, AuthMeColumns.NICK_NAME, realName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSourceValue<String> getEmail(String user) {
|
||||
try {
|
||||
return columnsHandler.retrieve(user, AuthMeColumns.EMAIL);
|
||||
} catch (SQLException e) {
|
||||
logSqlException(e);
|
||||
return DataSourceValueImpl.unknownRow();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package fr.xephi.authme.datasource;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValueImpl;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
@ -244,10 +246,10 @@ public class CacheDataSource implements DataSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSourceResult<String> getEmail(String user) {
|
||||
public DataSourceValue<String> getEmail(String user) {
|
||||
return cachedAuths.getUnchecked(user)
|
||||
.map(auth -> DataSourceResult.of(auth.getEmail()))
|
||||
.orElse(DataSourceResult.unknownPlayer());
|
||||
.map(auth -> DataSourceValueImpl.of(auth.getEmail()))
|
||||
.orElse(DataSourceValueImpl.unknownRow());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -268,6 +270,15 @@ public class CacheDataSource implements DataSource {
|
||||
return source.getRecentlyLoggedInPlayers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setTotpKey(String user, String totpKey) {
|
||||
boolean result = source.setTotpKey(user, totpKey);
|
||||
if (result) {
|
||||
cachedAuths.refresh(user);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCache(String playerName) {
|
||||
cachedAuths.invalidate(playerName);
|
||||
|
@ -14,6 +14,7 @@ public final class Columns {
|
||||
public final String REAL_NAME;
|
||||
public final String PASSWORD;
|
||||
public final String SALT;
|
||||
public final String TOTP_KEY;
|
||||
public final String LAST_IP;
|
||||
public final String LAST_LOGIN;
|
||||
public final String GROUP;
|
||||
@ -35,6 +36,7 @@ public final class Columns {
|
||||
REAL_NAME = settings.getProperty(DatabaseSettings.MYSQL_COL_REALNAME);
|
||||
PASSWORD = settings.getProperty(DatabaseSettings.MYSQL_COL_PASSWORD);
|
||||
SALT = settings.getProperty(DatabaseSettings.MYSQL_COL_SALT);
|
||||
TOTP_KEY = settings.getProperty(DatabaseSettings.MYSQL_COL_TOTP_KEY);
|
||||
LAST_IP = settings.getProperty(DatabaseSettings.MYSQL_COL_LAST_IP);
|
||||
LAST_LOGIN = settings.getProperty(DatabaseSettings.MYSQL_COL_LASTLOGIN);
|
||||
GROUP = settings.getProperty(DatabaseSettings.MYSQL_COL_GROUP);
|
||||
|
@ -1,5 +1,6 @@
|
||||
package fr.xephi.authme.datasource;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.initialization.Reloadable;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
@ -216,7 +217,7 @@ public interface DataSource extends Reloadable {
|
||||
* @param user the user to retrieve an email for
|
||||
* @return the email saved for the user, or null if user or email is not present
|
||||
*/
|
||||
DataSourceResult<String> getEmail(String user);
|
||||
DataSourceValue<String> getEmail(String user);
|
||||
|
||||
/**
|
||||
* Return all players of the database.
|
||||
@ -232,6 +233,25 @@ public interface DataSource extends Reloadable {
|
||||
*/
|
||||
List<PlayerAuth> getRecentlyLoggedInPlayers();
|
||||
|
||||
/**
|
||||
* Sets the given TOTP key to the player's account.
|
||||
*
|
||||
* @param user the name of the player to modify
|
||||
* @param totpKey the totp key to set
|
||||
* @return True upon success, false upon failure
|
||||
*/
|
||||
boolean setTotpKey(String user, String totpKey);
|
||||
|
||||
/**
|
||||
* Removes the TOTP key if present of the given player's account.
|
||||
*
|
||||
* @param user the name of the player to modify
|
||||
* @return True upon success, false upon failure
|
||||
*/
|
||||
default boolean removeTotpKey(String user) {
|
||||
return setTotpKey(user, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the data source.
|
||||
*/
|
||||
|
@ -1,53 +0,0 @@
|
||||
package fr.xephi.authme.datasource;
|
||||
|
||||
/**
|
||||
* Wraps a value and allows to specify whether a value is missing or the player is not registered.
|
||||
*/
|
||||
public final class DataSourceResult<T> {
|
||||
|
||||
/** Instance used when a player does not exist. */
|
||||
private static final DataSourceResult UNKNOWN_PLAYER = new DataSourceResult<>(null);
|
||||
private final T value;
|
||||
|
||||
private DataSourceResult(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link DataSourceResult} for the given value.
|
||||
*
|
||||
* @param value the value to wrap
|
||||
* @param <T> the value's type
|
||||
* @return DataSourceResult object for the given value
|
||||
*/
|
||||
public static <T> DataSourceResult<T> of(T value) {
|
||||
return new DataSourceResult<>(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link DataSourceResult} specifying that the player does not exist.
|
||||
*
|
||||
* @param <T> the value type
|
||||
* @return data source result for unknown player
|
||||
*/
|
||||
public static <T> DataSourceResult<T> unknownPlayer() {
|
||||
return UNKNOWN_PLAYER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the player of the associated value exists
|
||||
*/
|
||||
public boolean playerExists() {
|
||||
return this != UNKNOWN_PLAYER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value. It is {@code null} if the player is unknown. It is also {@code null}
|
||||
* if the player exists but does not have the value defined.
|
||||
*
|
||||
* @return the value, or null
|
||||
*/
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package fr.xephi.authme.datasource;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
@ -366,7 +367,7 @@ public class FlatFile implements DataSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSourceResult<String> getEmail(String user) {
|
||||
public DataSourceValue<String> getEmail(String user) {
|
||||
throw new UnsupportedOperationException("Flat file no longer supported");
|
||||
}
|
||||
|
||||
@ -398,6 +399,11 @@ public class FlatFile implements DataSource {
|
||||
throw new UnsupportedOperationException("Flat file no longer supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setTotpKey(String user, String totpKey) {
|
||||
throw new UnsupportedOperationException("Flat file no longer supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PlayerAuth object from the read data.
|
||||
*
|
||||
|
@ -5,14 +5,12 @@ import com.zaxxer.hikari.HikariDataSource;
|
||||
import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.columnshandler.AuthMeColumnsHandler;
|
||||
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension;
|
||||
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtensionsFactory;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
||||
import fr.xephi.authme.settings.properties.HooksSettings;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
@ -29,7 +27,11 @@ import java.util.Set;
|
||||
import static fr.xephi.authme.datasource.SqlDataSourceUtils.getNullableLong;
|
||||
import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException;
|
||||
|
||||
public class MySQL implements DataSource {
|
||||
/**
|
||||
* MySQL data source.
|
||||
*/
|
||||
@SuppressWarnings({"checkstyle:AbbreviationAsWordInName"}) // Justification: Class name cannot be changed anymore
|
||||
public class MySQL extends AbstractSqlDataSource {
|
||||
|
||||
private boolean useSsl;
|
||||
private String host;
|
||||
@ -96,6 +98,7 @@ public class MySQL implements DataSource {
|
||||
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
this.columnOthers = settings.getProperty(HooksSettings.MYSQL_OTHER_USERNAME_COLS);
|
||||
this.col = new Columns(settings);
|
||||
this.columnsHandler = AuthMeColumnsHandler.createForMySql(this::getConnection, settings);
|
||||
this.sqlExtension = extensionsFactory.buildExtension(col);
|
||||
this.poolSize = settings.getProperty(DatabaseSettings.MYSQL_POOL_SIZE);
|
||||
this.maxLifetime = settings.getProperty(DatabaseSettings.MYSQL_CONNECTION_MAX_LIFETIME);
|
||||
@ -249,6 +252,11 @@ public class MySQL implements DataSource {
|
||||
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
|
||||
+ col.HAS_SESSION + " SMALLINT NOT NULL DEFAULT '0' AFTER " + col.IS_LOGGED);
|
||||
}
|
||||
|
||||
if (isColumnMissing(md, col.TOTP_KEY)) {
|
||||
st.executeUpdate("ALTER TABLE " + tableName
|
||||
+ " ADD COLUMN " + col.TOTP_KEY + " VARCHAR(16);");
|
||||
}
|
||||
}
|
||||
ConsoleLogger.info("MySQL setup finished");
|
||||
}
|
||||
@ -259,40 +267,6 @@ public class MySQL implements DataSource {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthAvailable(String user) {
|
||||
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, user.toLowerCase());
|
||||
try (ResultSet rs = pst.executeQuery()) {
|
||||
return rs.next();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashedPassword getPassword(String user) {
|
||||
boolean useSalt = !col.SALT.isEmpty();
|
||||
String sql = "SELECT " + col.PASSWORD
|
||||
+ (useSalt ? ", " + col.SALT : "")
|
||||
+ " FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, user.toLowerCase());
|
||||
try (ResultSet rs = pst.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
return new HashedPassword(rs.getString(col.PASSWORD),
|
||||
useSalt ? rs.getString(col.SALT) : null);
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerAuth getAuth(String user) {
|
||||
String sql = "SELECT * FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
||||
@ -315,36 +289,13 @@ public class MySQL implements DataSource {
|
||||
|
||||
@Override
|
||||
public boolean saveAuth(PlayerAuth auth) {
|
||||
super.saveAuth(auth);
|
||||
|
||||
try (Connection con = getConnection()) {
|
||||
// TODO ljacqu 20171104: Replace with generic columns util to clean this up
|
||||
boolean useSalt = !col.SALT.isEmpty() || !StringUtils.isEmpty(auth.getPassword().getSalt());
|
||||
boolean hasEmail = auth.getEmail() != null;
|
||||
String emailPlaceholder = hasEmail ? "?" : "DEFAULT";
|
||||
|
||||
String sql = "INSERT INTO " + tableName + "("
|
||||
+ col.NAME + "," + col.PASSWORD + "," + col.REAL_NAME
|
||||
+ "," + col.EMAIL + "," + col.REGISTRATION_DATE + "," + col.REGISTRATION_IP
|
||||
+ (useSalt ? "," + col.SALT : "")
|
||||
+ ") VALUES (?,?,?," + emailPlaceholder + ",?,?" + (useSalt ? ",?" : "") + ");";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
int index = 1;
|
||||
pst.setString(index++, auth.getNickname());
|
||||
pst.setString(index++, auth.getPassword().getHash());
|
||||
pst.setString(index++, auth.getRealName());
|
||||
if (hasEmail) {
|
||||
pst.setString(index++, auth.getEmail());
|
||||
}
|
||||
pst.setObject(index++, auth.getRegistrationDate());
|
||||
pst.setString(index++, auth.getRegistrationIp());
|
||||
if (useSalt) {
|
||||
pst.setString(index++, auth.getPassword().getSalt());
|
||||
}
|
||||
pst.executeUpdate();
|
||||
}
|
||||
|
||||
if (!columnOthers.isEmpty()) {
|
||||
for (String column : columnOthers) {
|
||||
try (PreparedStatement pst = con.prepareStatement("UPDATE " + tableName + " SET " + column + "=? WHERE " + col.NAME + "=?;")) {
|
||||
try (PreparedStatement pst = con.prepareStatement(
|
||||
"UPDATE " + tableName + " SET " + column + "=? WHERE " + col.NAME + "=?;")) {
|
||||
pst.setString(1, auth.getRealName());
|
||||
pst.setString(2, auth.getNickname());
|
||||
pst.executeUpdate();
|
||||
@ -360,59 +311,6 @@ public class MySQL implements DataSource {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updatePassword(PlayerAuth auth) {
|
||||
return updatePassword(auth.getNickname(), auth.getPassword());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updatePassword(String user, HashedPassword password) {
|
||||
user = user.toLowerCase();
|
||||
try (Connection con = getConnection()) {
|
||||
boolean useSalt = !col.SALT.isEmpty();
|
||||
if (useSalt) {
|
||||
String sql = String.format("UPDATE %s SET %s = ?, %s = ? WHERE %s = ?;",
|
||||
tableName, col.PASSWORD, col.SALT, col.NAME);
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, password.getHash());
|
||||
pst.setString(2, password.getSalt());
|
||||
pst.setString(3, user);
|
||||
pst.executeUpdate();
|
||||
}
|
||||
} else {
|
||||
String sql = String.format("UPDATE %s SET %s = ? WHERE %s = ?;",
|
||||
tableName, col.PASSWORD, col.NAME);
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, password.getHash());
|
||||
pst.setString(2, user);
|
||||
pst.executeUpdate();
|
||||
}
|
||||
}
|
||||
sqlExtension.changePassword(user, password, con);
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateSession(PlayerAuth auth) {
|
||||
String sql = "UPDATE " + tableName + " SET "
|
||||
+ col.LAST_IP + "=?, " + col.LAST_LOGIN + "=?, " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, auth.getLastIp());
|
||||
pst.setObject(2, auth.getLastLogin());
|
||||
pst.setString(3, auth.getRealName());
|
||||
pst.setString(4, auth.getNickname());
|
||||
pst.executeUpdate();
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRecordsToPurge(long until) {
|
||||
Set<String> list = new HashSet<>();
|
||||
@ -450,42 +348,6 @@ public class MySQL implements DataSource {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateQuitLoc(PlayerAuth auth) {
|
||||
String sql = "UPDATE " + tableName
|
||||
+ " SET " + col.LASTLOC_X + " =?, " + col.LASTLOC_Y + "=?, " + col.LASTLOC_Z + "=?, "
|
||||
+ col.LASTLOC_WORLD + "=?, " + col.LASTLOC_YAW + "=?, " + col.LASTLOC_PITCH + "=?"
|
||||
+ " WHERE " + col.NAME + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setDouble(1, auth.getQuitLocX());
|
||||
pst.setDouble(2, auth.getQuitLocY());
|
||||
pst.setDouble(3, auth.getQuitLocZ());
|
||||
pst.setString(4, auth.getWorld());
|
||||
pst.setFloat(5, auth.getYaw());
|
||||
pst.setFloat(6, auth.getPitch());
|
||||
pst.setString(7, auth.getNickname());
|
||||
pst.executeUpdate();
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateEmail(PlayerAuth auth) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.EMAIL + " =? WHERE " + col.NAME + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, auth.getEmail());
|
||||
pst.setString(2, auth.getNickname());
|
||||
pst.executeUpdate();
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeConnection() {
|
||||
if (ds != null && !ds.isClosed()) {
|
||||
@ -493,39 +355,6 @@ public class MySQL implements DataSource {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllAuthsByIp(String ip) {
|
||||
List<String> result = new ArrayList<>();
|
||||
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_IP + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, ip);
|
||||
try (ResultSet rs = pst.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
result.add(rs.getString(col.NAME));
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countAuthsByEmail(String email) {
|
||||
String sql = "SELECT COUNT(1) FROM " + tableName + " WHERE UPPER(" + col.EMAIL + ") = UPPER(?)";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, email);
|
||||
try (ResultSet rs = pst.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
return rs.getInt(1);
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void purgeRecords(Collection<String> toPurge) {
|
||||
String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
||||
@ -544,140 +373,6 @@ public class MySQL implements DataSource {
|
||||
return DataSourceType.MYSQL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogged(String user) {
|
||||
String sql = "SELECT " + col.IS_LOGGED + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, user);
|
||||
try (ResultSet rs = pst.executeQuery()) {
|
||||
return rs.next() && (rs.getInt(col.IS_LOGGED) == 1);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogged(String user) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setInt(1, 1);
|
||||
pst.setString(2, user.toLowerCase());
|
||||
pst.executeUpdate();
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUnlogged(String user) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setInt(1, 0);
|
||||
pst.setString(2, user.toLowerCase());
|
||||
pst.executeUpdate();
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSession(String user) {
|
||||
String sql = "SELECT " + col.HAS_SESSION + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, user.toLowerCase());
|
||||
try (ResultSet rs = pst.executeQuery()) {
|
||||
return rs.next() && (rs.getInt(col.HAS_SESSION) == 1);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantSession(String user) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.HAS_SESSION + "=? WHERE " + col.NAME + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setInt(1, 1);
|
||||
pst.setString(2, user.toLowerCase());
|
||||
pst.executeUpdate();
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeSession(String user) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.HAS_SESSION + "=? WHERE " + col.NAME + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setInt(1, 0);
|
||||
pst.setString(2, user.toLowerCase());
|
||||
pst.executeUpdate();
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void purgeLogged() {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.IS_LOGGED + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setInt(1, 0);
|
||||
pst.setInt(2, 1);
|
||||
pst.executeUpdate();
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccountsRegistered() {
|
||||
int result = 0;
|
||||
String sql = "SELECT COUNT(*) FROM " + tableName;
|
||||
try (Connection con = getConnection();
|
||||
Statement st = con.createStatement();
|
||||
ResultSet rs = st.executeQuery(sql)) {
|
||||
if (rs.next()) {
|
||||
result = rs.getInt(1);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateRealName(String user, String realName) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, realName);
|
||||
pst.setString(2, user);
|
||||
pst.executeUpdate();
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSourceResult<String> getEmail(String user) {
|
||||
String sql = "SELECT " + col.EMAIL + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, user);
|
||||
try (ResultSet rs = pst.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
return DataSourceResult.of(rs.getString(1));
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return DataSourceResult.unknownPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PlayerAuth> getAllAuths() {
|
||||
List<PlayerAuth> auths = new ArrayList<>();
|
||||
@ -728,6 +423,27 @@ public class MySQL implements DataSource {
|
||||
return players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setTotpKey(String user, String totpKey) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.TOTP_KEY + " = ? WHERE " + col.NAME + " = ?";
|
||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, totpKey);
|
||||
pst.setString(2, user.toLowerCase());
|
||||
pst.executeUpdate();
|
||||
return true;
|
||||
} catch (SQLException e) {
|
||||
logSqlException(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link PlayerAuth} object with the data from the provided result set.
|
||||
*
|
||||
* @param row the result set to read from
|
||||
* @return generated player auth object with the data from the result set
|
||||
* @throws SQLException .
|
||||
*/
|
||||
private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException {
|
||||
String salt = col.SALT.isEmpty() ? null : row.getString(col.SALT);
|
||||
int group = col.GROUP.isEmpty() ? -1 : row.getInt(col.GROUP);
|
||||
@ -735,6 +451,7 @@ public class MySQL implements DataSource {
|
||||
.name(row.getString(col.NAME))
|
||||
.realName(row.getString(col.REAL_NAME))
|
||||
.password(row.getString(col.PASSWORD), salt)
|
||||
.totpKey(row.getString(col.TOTP_KEY))
|
||||
.lastLogin(getNullableLong(row, col.LAST_LOGIN))
|
||||
.lastIp(row.getString(col.LAST_IP))
|
||||
.email(row.getString(col.EMAIL))
|
||||
|
@ -3,10 +3,9 @@ package fr.xephi.authme.datasource;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.datasource.columnshandler.AuthMeColumnsHandler;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
@ -28,7 +27,8 @@ import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException;
|
||||
/**
|
||||
* SQLite data source.
|
||||
*/
|
||||
public class SQLite implements DataSource {
|
||||
@SuppressWarnings({"checkstyle:AbbreviationAsWordInName"}) // Justification: Class name cannot be changed anymore
|
||||
public class SQLite extends AbstractSqlDataSource {
|
||||
|
||||
private final Settings settings;
|
||||
private final File dataFolder;
|
||||
@ -41,6 +41,7 @@ public class SQLite implements DataSource {
|
||||
* Constructor for SQLite.
|
||||
*
|
||||
* @param settings The settings instance
|
||||
* @param dataFolder The data folder
|
||||
*
|
||||
* @throws SQLException when initialization of a SQL datasource failed
|
||||
*/
|
||||
@ -69,6 +70,7 @@ public class SQLite implements DataSource {
|
||||
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
this.col = new Columns(settings);
|
||||
this.con = connection;
|
||||
this.columnsHandler = AuthMeColumnsHandler.createForSqlite(con, settings);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,6 +85,7 @@ public class SQLite implements DataSource {
|
||||
|
||||
ConsoleLogger.debug("SQLite driver loaded");
|
||||
this.con = DriverManager.getConnection("jdbc:sqlite:plugins/AuthMe/" + database + ".db");
|
||||
this.columnsHandler = AuthMeColumnsHandler.createForSqlite(con, settings);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -170,6 +173,11 @@ public class SQLite implements DataSource {
|
||||
st.executeUpdate("ALTER TABLE " + tableName
|
||||
+ " ADD COLUMN " + col.HAS_SESSION + " INT NOT NULL DEFAULT '0';");
|
||||
}
|
||||
|
||||
if (isColumnMissing(md, col.TOTP_KEY)) {
|
||||
st.executeUpdate("ALTER TABLE " + tableName
|
||||
+ " ADD COLUMN " + col.TOTP_KEY + " VARCHAR(16);");
|
||||
}
|
||||
}
|
||||
ConsoleLogger.info("SQLite Setup finished");
|
||||
}
|
||||
@ -202,40 +210,6 @@ public class SQLite implements DataSource {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthAvailable(String user) {
|
||||
String sql = "SELECT 1 FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=LOWER(?);";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, user);
|
||||
try (ResultSet rs = pst.executeQuery()) {
|
||||
return rs.next();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ConsoleLogger.warning(ex.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashedPassword getPassword(String user) {
|
||||
boolean useSalt = !col.SALT.isEmpty();
|
||||
String sql = "SELECT " + col.PASSWORD
|
||||
+ (useSalt ? ", " + col.SALT : "")
|
||||
+ " FROM " + tableName + " WHERE " + col.NAME + "=?";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, user);
|
||||
try (ResultSet rs = pst.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
return new HashedPassword(rs.getString(col.PASSWORD),
|
||||
useSalt ? rs.getString(col.SALT) : null);
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerAuth getAuth(String user) {
|
||||
String sql = "SELECT * FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=LOWER(?);";
|
||||
@ -252,95 +226,6 @@ public class SQLite implements DataSource {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveAuth(PlayerAuth auth) {
|
||||
PreparedStatement pst = null;
|
||||
try {
|
||||
HashedPassword password = auth.getPassword();
|
||||
if (col.SALT.isEmpty()) {
|
||||
if (!StringUtils.isEmpty(auth.getPassword().getSalt())) {
|
||||
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
|
||||
+ "," + col.REAL_NAME + "," + col.EMAIL
|
||||
+ "," + col.REGISTRATION_DATE + "," + col.REGISTRATION_IP
|
||||
+ ") VALUES (?,?,?,?,?,?);");
|
||||
pst.setString(1, auth.getNickname());
|
||||
pst.setString(2, password.getHash());
|
||||
pst.setString(3, auth.getRealName());
|
||||
pst.setString(4, auth.getEmail());
|
||||
pst.setLong(5, auth.getRegistrationDate());
|
||||
pst.setString(6, auth.getRegistrationIp());
|
||||
pst.executeUpdate();
|
||||
} else {
|
||||
pst = con.prepareStatement("INSERT INTO " + tableName + "(" + col.NAME + "," + col.PASSWORD
|
||||
+ "," + col.REAL_NAME + "," + col.EMAIL
|
||||
+ "," + col.REGISTRATION_DATE + "," + col.REGISTRATION_IP + "," + col.SALT
|
||||
+ ") VALUES (?,?,?,?,?,?,?);");
|
||||
pst.setString(1, auth.getNickname());
|
||||
pst.setString(2, password.getHash());
|
||||
pst.setString(3, auth.getRealName());
|
||||
pst.setString(4, auth.getEmail());
|
||||
pst.setLong(5, auth.getRegistrationDate());
|
||||
pst.setString(6, auth.getRegistrationIp());
|
||||
pst.setString(7, password.getSalt());
|
||||
pst.executeUpdate();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
} finally {
|
||||
close(pst);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updatePassword(PlayerAuth auth) {
|
||||
return updatePassword(auth.getNickname(), auth.getPassword());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updatePassword(String user, HashedPassword password) {
|
||||
user = user.toLowerCase();
|
||||
boolean useSalt = !col.SALT.isEmpty();
|
||||
String sql = "UPDATE " + tableName + " SET " + col.PASSWORD + " = ?"
|
||||
+ (useSalt ? ", " + col.SALT + " = ?" : "")
|
||||
+ " WHERE " + col.NAME + " = ?";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)){
|
||||
pst.setString(1, password.getHash());
|
||||
if (useSalt) {
|
||||
pst.setString(2, password.getSalt());
|
||||
pst.setString(3, user);
|
||||
} else {
|
||||
pst.setString(2, user);
|
||||
}
|
||||
pst.executeUpdate();
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateSession(PlayerAuth auth) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.LAST_IP + "=?, " + col.LAST_LOGIN + "=?, "
|
||||
+ col.REAL_NAME + "=? WHERE " + col.NAME + "=?;";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)){
|
||||
pst.setString(1, auth.getLastIp());
|
||||
pst.setObject(2, auth.getLastLogin());
|
||||
pst.setString(3, auth.getRealName());
|
||||
pst.setString(4, auth.getNickname());
|
||||
pst.executeUpdate();
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRecordsToPurge(long until) {
|
||||
Set<String> list = new HashSet<>();
|
||||
@ -388,42 +273,6 @@ public class SQLite implements DataSource {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateQuitLoc(PlayerAuth auth) {
|
||||
String sql = "UPDATE " + tableName + " SET "
|
||||
+ col.LASTLOC_X + "=?, " + col.LASTLOC_Y + "=?, " + col.LASTLOC_Z + "=?, "
|
||||
+ col.LASTLOC_WORLD + "=?, " + col.LASTLOC_YAW + "=?, " + col.LASTLOC_PITCH + "=? "
|
||||
+ "WHERE " + col.NAME + "=?;";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setDouble(1, auth.getQuitLocX());
|
||||
pst.setDouble(2, auth.getQuitLocY());
|
||||
pst.setDouble(3, auth.getQuitLocZ());
|
||||
pst.setString(4, auth.getWorld());
|
||||
pst.setFloat(5, auth.getYaw());
|
||||
pst.setFloat(6, auth.getPitch());
|
||||
pst.setString(7, auth.getNickname());
|
||||
pst.executeUpdate();
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateEmail(PlayerAuth auth) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.EMAIL + "=? WHERE " + col.NAME + "=?;";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, auth.getEmail());
|
||||
pst.setString(2, auth.getNickname());
|
||||
pst.executeUpdate();
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeConnection() {
|
||||
try {
|
||||
@ -435,180 +284,11 @@ public class SQLite implements DataSource {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllAuthsByIp(String ip) {
|
||||
List<String> countIp = new ArrayList<>();
|
||||
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_IP + "=?;";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, ip);
|
||||
try (ResultSet rs = pst.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
countIp.add(rs.getString(col.NAME));
|
||||
}
|
||||
return countIp;
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countAuthsByEmail(String email) {
|
||||
String sql = "SELECT COUNT(1) FROM " + tableName + " WHERE " + col.EMAIL + " = ? COLLATE NOCASE;";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, email);
|
||||
try (ResultSet rs = pst.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
return rs.getInt(1);
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSourceType getType() {
|
||||
return DataSourceType.SQLITE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogged(String user) {
|
||||
String sql = "SELECT " + col.IS_LOGGED + " FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=?;";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, user);
|
||||
try (ResultSet rs = pst.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
return rs.getInt(col.IS_LOGGED) == 1;
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogged(String user) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE LOWER(" + col.NAME + ")=?;";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setInt(1, 1);
|
||||
pst.setString(2, user);
|
||||
pst.executeUpdate();
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUnlogged(String user) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE LOWER(" + col.NAME + ")=?;";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setInt(1, 0);
|
||||
pst.setString(2, user);
|
||||
pst.executeUpdate();
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSession(String user) {
|
||||
String sql = "SELECT " + col.HAS_SESSION + " FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=?;";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, user.toLowerCase());
|
||||
try (ResultSet rs = pst.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
return rs.getInt(col.HAS_SESSION) == 1;
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantSession(String user) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.HAS_SESSION + "=? WHERE LOWER(" + col.NAME + ")=?;";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setInt(1, 1);
|
||||
pst.setString(2, user.toLowerCase());
|
||||
pst.executeUpdate();
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeSession(String user) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.HAS_SESSION + "=? WHERE LOWER(" + col.NAME + ")=?;";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setInt(1, 0);
|
||||
pst.setString(2, user.toLowerCase());
|
||||
pst.executeUpdate();
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void purgeLogged() {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.IS_LOGGED + "=?;";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setInt(1, 0);
|
||||
pst.setInt(2, 1);
|
||||
pst.executeUpdate();
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccountsRegistered() {
|
||||
String sql = "SELECT COUNT(*) FROM " + tableName + ";";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql); ResultSet rs = pst.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
return rs.getInt(1);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateRealName(String user, String realName) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, realName);
|
||||
pst.setString(2, user);
|
||||
pst.executeUpdate();
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSourceResult<String> getEmail(String user) {
|
||||
String sql = "SELECT " + col.EMAIL + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, user);
|
||||
try (ResultSet rs = pst.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
return DataSourceResult.of(rs.getString(1));
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
return DataSourceResult.unknownPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PlayerAuth> getAllAuths() {
|
||||
List<PlayerAuth> auths = new ArrayList<>();
|
||||
@ -653,6 +333,21 @@ public class SQLite implements DataSource {
|
||||
return players;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean setTotpKey(String user, String totpKey) {
|
||||
String sql = "UPDATE " + tableName + " SET " + col.TOTP_KEY + " = ? WHERE " + col.NAME + " = ?";
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setString(1, totpKey);
|
||||
pst.setString(2, user.toLowerCase());
|
||||
pst.executeUpdate();
|
||||
return true;
|
||||
} catch (SQLException e) {
|
||||
logSqlException(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException {
|
||||
String salt = !col.SALT.isEmpty() ? row.getString(col.SALT) : null;
|
||||
|
||||
@ -661,6 +356,7 @@ public class SQLite implements DataSource {
|
||||
.email(row.getString(col.EMAIL))
|
||||
.realName(row.getString(col.REAL_NAME))
|
||||
.password(row.getString(col.PASSWORD), salt)
|
||||
.totpKey(row.getString(col.TOTP_KEY))
|
||||
.lastLogin(getNullableLong(row, col.LAST_LOGIN))
|
||||
.lastIp(row.getString(col.LAST_IP))
|
||||
.registrationDate(row.getLong(col.REGISTRATION_DATE))
|
||||
@ -693,16 +389,6 @@ public class SQLite implements DataSource {
|
||||
+ currentTimestamp + ", to all " + updatedRows + " rows");
|
||||
}
|
||||
|
||||
private static void close(Statement st) {
|
||||
if (st != null) {
|
||||
try {
|
||||
st.close();
|
||||
} catch (SQLException ex) {
|
||||
logSqlException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void close(Connection con) {
|
||||
if (con != null) {
|
||||
try {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package fr.xephi.authme.datasource;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
||||
@ -8,14 +9,10 @@ import fr.xephi.authme.util.FileUtils;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Files;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Migrates the SQLite database when necessary.
|
||||
@ -70,11 +67,10 @@ class SqLiteMigrater {
|
||||
File backupDirectory = new File(dataFolder, "backups");
|
||||
FileUtils.createDirectory(backupDirectory);
|
||||
|
||||
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm");
|
||||
String backupName = "backup-" + databaseName + dateFormat.format(new Date()) + ".db";
|
||||
String backupName = "backup-" + databaseName + FileUtils.createCurrentTimeString() + ".db";
|
||||
File backup = new File(backupDirectory, backupName);
|
||||
try {
|
||||
Files.copy(sqLite.toPath(), backup.toPath());
|
||||
Files.copy(sqLite, backup);
|
||||
return backupName;
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to create SQLite backup before migration", e);
|
||||
|
@ -0,0 +1,82 @@
|
||||
package fr.xephi.authme.datasource.columnshandler;
|
||||
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
||||
|
||||
import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.ColumnOptions.DEFAULT_FOR_NULL;
|
||||
import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.ColumnOptions.OPTIONAL;
|
||||
import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.createDouble;
|
||||
import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.createFloat;
|
||||
import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.createInteger;
|
||||
import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.createLong;
|
||||
import static fr.xephi.authme.datasource.columnshandler.AuthMeColumnsFactory.createString;
|
||||
|
||||
/**
|
||||
* Contains column definitions for the AuthMe table.
|
||||
*/
|
||||
public final class AuthMeColumns {
|
||||
|
||||
public static final PlayerAuthColumn<String> NAME = createString(
|
||||
DatabaseSettings.MYSQL_COL_NAME, PlayerAuth::getNickname);
|
||||
|
||||
public static final PlayerAuthColumn<String> NICK_NAME = createString(
|
||||
DatabaseSettings.MYSQL_COL_REALNAME, PlayerAuth::getRealName);
|
||||
|
||||
public static final PlayerAuthColumn<String> PASSWORD = createString(
|
||||
DatabaseSettings.MYSQL_COL_PASSWORD, auth -> auth.getPassword().getHash());
|
||||
|
||||
public static final PlayerAuthColumn<String> SALT = createString(
|
||||
DatabaseSettings.MYSQL_COL_SALT, auth -> auth.getPassword().getSalt(), OPTIONAL);
|
||||
|
||||
public static final PlayerAuthColumn<String> EMAIL = createString(
|
||||
DatabaseSettings.MYSQL_COL_EMAIL, PlayerAuth::getEmail, DEFAULT_FOR_NULL);
|
||||
|
||||
public static final PlayerAuthColumn<String> LAST_IP = createString(
|
||||
DatabaseSettings.MYSQL_COL_LAST_IP, PlayerAuth::getLastIp);
|
||||
|
||||
public static final PlayerAuthColumn<Integer> GROUP_ID = createInteger(
|
||||
DatabaseSettings.MYSQL_COL_GROUP, PlayerAuth::getGroupId, OPTIONAL);
|
||||
|
||||
public static final PlayerAuthColumn<Long> LAST_LOGIN = createLong(
|
||||
DatabaseSettings.MYSQL_COL_LASTLOGIN, PlayerAuth::getLastLogin);
|
||||
|
||||
public static final PlayerAuthColumn<String> REGISTRATION_IP = createString(
|
||||
DatabaseSettings.MYSQL_COL_REGISTER_IP, PlayerAuth::getRegistrationIp);
|
||||
|
||||
public static final PlayerAuthColumn<Long> REGISTRATION_DATE = createLong(
|
||||
DatabaseSettings.MYSQL_COL_REGISTER_DATE, PlayerAuth::getRegistrationDate);
|
||||
|
||||
// --------
|
||||
// Location columns
|
||||
// --------
|
||||
public static final PlayerAuthColumn<Double> LOCATION_X = createDouble(
|
||||
DatabaseSettings.MYSQL_COL_LASTLOC_X, PlayerAuth::getQuitLocX);
|
||||
|
||||
public static final PlayerAuthColumn<Double> LOCATION_Y = createDouble(
|
||||
DatabaseSettings.MYSQL_COL_LASTLOC_Y, PlayerAuth::getQuitLocY);
|
||||
|
||||
public static final PlayerAuthColumn<Double> LOCATION_Z = createDouble(
|
||||
DatabaseSettings.MYSQL_COL_LASTLOC_Z, PlayerAuth::getQuitLocZ);
|
||||
|
||||
public static final PlayerAuthColumn<String> LOCATION_WORLD = createString(
|
||||
DatabaseSettings.MYSQL_COL_LASTLOC_WORLD, PlayerAuth::getWorld);
|
||||
|
||||
public static final PlayerAuthColumn<Float> LOCATION_YAW = createFloat(
|
||||
DatabaseSettings.MYSQL_COL_LASTLOC_YAW, PlayerAuth::getYaw);
|
||||
|
||||
public static final PlayerAuthColumn<Float> LOCATION_PITCH = createFloat(
|
||||
DatabaseSettings.MYSQL_COL_LASTLOC_PITCH, PlayerAuth::getPitch);
|
||||
|
||||
// --------
|
||||
// Columns not on PlayerAuth
|
||||
// --------
|
||||
public static final DataSourceColumn<Integer> IS_LOGGED = createInteger(
|
||||
DatabaseSettings.MYSQL_COL_ISLOGGED);
|
||||
|
||||
public static final DataSourceColumn<Integer> HAS_SESSION = createInteger(
|
||||
DatabaseSettings.MYSQL_COL_HASSESSION);
|
||||
|
||||
|
||||
private AuthMeColumns() {
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package fr.xephi.authme.datasource.columnshandler;
|
||||
|
||||
import ch.jalu.configme.properties.Property;
|
||||
import ch.jalu.datasourcecolumns.ColumnType;
|
||||
import ch.jalu.datasourcecolumns.StandardTypes;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Util class for initializing {@link DataSourceColumn} objects.
|
||||
*/
|
||||
final class AuthMeColumnsFactory {
|
||||
|
||||
private AuthMeColumnsFactory() {
|
||||
}
|
||||
|
||||
static DataSourceColumn<Integer> createInteger(Property<String> nameProperty,
|
||||
ColumnOptions... options) {
|
||||
return new DataSourceColumn<>(StandardTypes.INTEGER, nameProperty,
|
||||
isOptional(options), hasDefaultForNull(options));
|
||||
}
|
||||
|
||||
static PlayerAuthColumn<Integer> createInteger(Property<String> nameProperty,
|
||||
Function<PlayerAuth, Integer> playerAuthGetter,
|
||||
ColumnOptions... options) {
|
||||
return createInternal(StandardTypes.INTEGER, nameProperty, playerAuthGetter, options);
|
||||
}
|
||||
|
||||
static PlayerAuthColumn<Long> createLong(Property<String> nameProperty,
|
||||
Function<PlayerAuth, Long> playerAuthGetter,
|
||||
ColumnOptions... options) {
|
||||
return createInternal(StandardTypes.LONG, nameProperty, playerAuthGetter, options);
|
||||
}
|
||||
|
||||
static PlayerAuthColumn<String> createString(Property<String> nameProperty,
|
||||
Function<PlayerAuth, String> playerAuthGetter,
|
||||
ColumnOptions... options) {
|
||||
return createInternal(StandardTypes.STRING, nameProperty, playerAuthGetter, options);
|
||||
}
|
||||
|
||||
static PlayerAuthColumn<Double> createDouble(Property<String> nameProperty,
|
||||
Function<PlayerAuth, Double> playerAuthGetter,
|
||||
ColumnOptions... options) {
|
||||
return createInternal(StandardTypes.DOUBLE, nameProperty, playerAuthGetter, options);
|
||||
}
|
||||
|
||||
static PlayerAuthColumn<Float> createFloat(Property<String> nameProperty,
|
||||
Function<PlayerAuth, Float> playerAuthGetter,
|
||||
ColumnOptions... options) {
|
||||
return createInternal(StandardTypes.FLOAT, nameProperty, playerAuthGetter, options);
|
||||
}
|
||||
|
||||
private static <T> PlayerAuthColumn<T> createInternal(ColumnType<T> type, Property<String> nameProperty,
|
||||
Function<PlayerAuth, T> authGetter,
|
||||
ColumnOptions... options) {
|
||||
return new PlayerAuthColumn<>(type, nameProperty, isOptional(options), hasDefaultForNull(options), authGetter);
|
||||
}
|
||||
|
||||
private static boolean isOptional(ColumnOptions[] options) {
|
||||
return containsInArray(ColumnOptions.OPTIONAL, options);
|
||||
}
|
||||
|
||||
private static boolean hasDefaultForNull(ColumnOptions[] options) {
|
||||
return containsInArray(ColumnOptions.DEFAULT_FOR_NULL, options);
|
||||
}
|
||||
|
||||
private static boolean containsInArray(ColumnOptions needle, ColumnOptions[] haystack) {
|
||||
for (ColumnOptions option : haystack) {
|
||||
if (option == needle) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
enum ColumnOptions {
|
||||
|
||||
OPTIONAL,
|
||||
|
||||
DEFAULT_FOR_NULL
|
||||
}
|
||||
}
|
@ -0,0 +1,206 @@
|
||||
package fr.xephi.authme.datasource.columnshandler;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValues;
|
||||
import ch.jalu.datasourcecolumns.data.UpdateValues;
|
||||
import ch.jalu.datasourcecolumns.predicate.Predicate;
|
||||
import ch.jalu.datasourcecolumns.sqlimplementation.PredicateSqlGenerator;
|
||||
import ch.jalu.datasourcecolumns.sqlimplementation.SqlColumnsHandler;
|
||||
import ch.jalu.datasourcecolumns.sqlimplementation.statementgenerator.ConnectionSupplier;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import static ch.jalu.datasourcecolumns.sqlimplementation.SqlColumnsHandlerConfig.forConnectionPool;
|
||||
import static ch.jalu.datasourcecolumns.sqlimplementation.SqlColumnsHandlerConfig.forSingleConnection;
|
||||
import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException;
|
||||
|
||||
/**
|
||||
* Wrapper of {@link SqlColumnsHandler} for the AuthMe data table.
|
||||
* Wraps exceptions and provides better support for operations based on a {@link PlayerAuth} object.
|
||||
*/
|
||||
public final class AuthMeColumnsHandler {
|
||||
|
||||
private final SqlColumnsHandler<ColumnContext, String> internalHandler;
|
||||
|
||||
private AuthMeColumnsHandler(SqlColumnsHandler<ColumnContext, String> internalHandler) {
|
||||
this.internalHandler = internalHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a column handler for SQLite.
|
||||
*
|
||||
* @param connection the connection to the database
|
||||
* @param settings plugin settings
|
||||
* @return created column handler
|
||||
*/
|
||||
public static AuthMeColumnsHandler createForSqlite(Connection connection, Settings settings) {
|
||||
ColumnContext columnContext = new ColumnContext(settings, false);
|
||||
String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
String nameColumn = settings.getProperty(DatabaseSettings.MYSQL_COL_NAME);
|
||||
|
||||
SqlColumnsHandler<ColumnContext, String> sqlColHandler = new SqlColumnsHandler<>(
|
||||
forSingleConnection(connection, tableName, nameColumn, columnContext)
|
||||
.setPredicateSqlGenerator(new PredicateSqlGenerator<>(columnContext, true))
|
||||
);
|
||||
return new AuthMeColumnsHandler(sqlColHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a column handler for MySQL.
|
||||
*
|
||||
* @param connectionSupplier supplier of connections from the connection pool
|
||||
* @param settings plugin settings
|
||||
* @return created column handler
|
||||
*/
|
||||
public static AuthMeColumnsHandler createForMySql(ConnectionSupplier connectionSupplier, Settings settings) {
|
||||
ColumnContext columnContext = new ColumnContext(settings, true);
|
||||
String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
String nameColumn = settings.getProperty(DatabaseSettings.MYSQL_COL_NAME);
|
||||
|
||||
SqlColumnsHandler<ColumnContext, String> sqlColHandler = new SqlColumnsHandler<>(
|
||||
forConnectionPool(connectionSupplier, tableName, nameColumn, columnContext));
|
||||
return new AuthMeColumnsHandler(sqlColHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes a column from a specific row to the given value.
|
||||
*
|
||||
* @param name name of the account to modify
|
||||
* @param column the column to modify
|
||||
* @param value the value to set the column to
|
||||
* @param <T> the column type
|
||||
* @return true upon success, false otherwise
|
||||
*/
|
||||
public <T> boolean update(String name, DataSourceColumn<T> column, T value) {
|
||||
try {
|
||||
return internalHandler.update(name, column, value);
|
||||
} catch (SQLException e) {
|
||||
logSqlException(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a row to have the values as retrieved from the PlayerAuth object.
|
||||
*
|
||||
* @param auth the player auth object to modify and to get values from
|
||||
* @param columns the columns to update in the row
|
||||
* @return true upon success, false otherwise
|
||||
*/
|
||||
public boolean update(PlayerAuth auth, PlayerAuthColumn<?>... columns) {
|
||||
try {
|
||||
return internalHandler.update(auth.getNickname(), auth, columns);
|
||||
} catch (SQLException e) {
|
||||
logSqlException(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a row to have the given values.
|
||||
*
|
||||
* @param name the name of the account to modify
|
||||
* @param updateValues the values to set on the row
|
||||
* @return true upon success, false otherwise
|
||||
*/
|
||||
public boolean update(String name, UpdateValues<ColumnContext> updateValues) {
|
||||
try {
|
||||
return internalHandler.update(name.toLowerCase(), updateValues);
|
||||
} catch (SQLException e) {
|
||||
logSqlException(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given value to the provided column for all rows which match the predicate.
|
||||
*
|
||||
* @param predicate the predicate to filter rows by
|
||||
* @param column the column to modify on the matched rows
|
||||
* @param value the new value to set
|
||||
* @param <T> the column type
|
||||
* @return number of modified rows
|
||||
*/
|
||||
public <T> int update(Predicate<ColumnContext> predicate, DataSourceColumn<T> column, T value) {
|
||||
try {
|
||||
return internalHandler.update(predicate, column, value);
|
||||
} catch (SQLException e) {
|
||||
logSqlException(e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the given column from a given row.
|
||||
*
|
||||
* @param name the account name to look up
|
||||
* @param column the column whose value should be retrieved
|
||||
* @param <T> the column type
|
||||
* @return the result of the lookup
|
||||
* @throws SQLException .
|
||||
*/
|
||||
public <T> DataSourceValue<T> retrieve(String name, DataSourceColumn<T> column) throws SQLException {
|
||||
return internalHandler.retrieve(name.toLowerCase(), column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves multiple values from a given row.
|
||||
*
|
||||
* @param name the account name to look up
|
||||
* @param columns the columns to retrieve
|
||||
* @return map-like object with the requested values
|
||||
* @throws SQLException .
|
||||
*/
|
||||
public DataSourceValues retrieve(String name, DataSourceColumn<?>... columns) throws SQLException {
|
||||
return internalHandler.retrieve(name.toLowerCase(), columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a column's value for all rows that satisfy the given predicate.
|
||||
*
|
||||
* @param predicate the predicate to fulfill
|
||||
* @param column the column to retrieve from the matching rows
|
||||
* @param <T> the column's value type
|
||||
* @return the values of the matching rows
|
||||
* @throws SQLException .
|
||||
*/
|
||||
public <T> List<T> retrieve(Predicate<ColumnContext> predicate, DataSourceColumn<T> column) throws SQLException {
|
||||
return internalHandler.retrieve(predicate, column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the given values into a new row, as taken from the player auth.
|
||||
*
|
||||
* @param auth the player auth to get values from
|
||||
* @param columns the columns to insert
|
||||
* @return true upon success, false otherwise
|
||||
*/
|
||||
public boolean insert(PlayerAuth auth, PlayerAuthColumn<?>... columns) {
|
||||
try {
|
||||
return internalHandler.insert(auth, columns);
|
||||
} catch (SQLException e) {
|
||||
logSqlException(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of rows that match the provided predicate.
|
||||
*
|
||||
* @param predicate the predicate to test the rows for
|
||||
* @return number of rows fulfilling the predicate
|
||||
*/
|
||||
public int count(Predicate<ColumnContext> predicate) {
|
||||
try {
|
||||
return internalHandler.count(predicate);
|
||||
} catch (SQLException e) {
|
||||
logSqlException(e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package fr.xephi.authme.datasource.columnshandler;
|
||||
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Context for resolving the properties of {@link AuthMeColumns} entries.
|
||||
*/
|
||||
public class ColumnContext {
|
||||
|
||||
private final Settings settings;
|
||||
private final Map<DataSourceColumn<?>, String> columnNames = new HashMap<>();
|
||||
private final boolean hasDefaultSupport;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param settings plugin settings
|
||||
* @param hasDefaultSupport whether or not the underlying database has support for the {@code DEFAULT} keyword
|
||||
*/
|
||||
public ColumnContext(Settings settings, boolean hasDefaultSupport) {
|
||||
this.settings = settings;
|
||||
this.hasDefaultSupport = hasDefaultSupport;
|
||||
}
|
||||
|
||||
public String getName(DataSourceColumn<?> column) {
|
||||
return columnNames.computeIfAbsent(column, k -> settings.getProperty(k.getNameProperty()));
|
||||
}
|
||||
|
||||
public boolean hasDefaultSupport() {
|
||||
return hasDefaultSupport;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package fr.xephi.authme.datasource.columnshandler;
|
||||
|
||||
import ch.jalu.configme.properties.Property;
|
||||
import ch.jalu.datasourcecolumns.Column;
|
||||
import ch.jalu.datasourcecolumns.ColumnType;
|
||||
|
||||
/**
|
||||
* Basic {@link Column} implementation for AuthMe.
|
||||
*
|
||||
* @param <T> column type
|
||||
*/
|
||||
public class DataSourceColumn<T> implements Column<T, ColumnContext> {
|
||||
|
||||
private final ColumnType<T> columnType;
|
||||
private final Property<String> nameProperty;
|
||||
private final boolean isOptional;
|
||||
private final boolean useDefaultForNull;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param type type of the column
|
||||
* @param nameProperty property defining the column name
|
||||
* @param isOptional whether or not the column can be skipped (if name is configured to empty string)
|
||||
* @param useDefaultForNull whether SQL DEFAULT should be used for null values (if supported by the database)
|
||||
*/
|
||||
DataSourceColumn(ColumnType<T> type, Property<String> nameProperty, boolean isOptional, boolean useDefaultForNull) {
|
||||
this.columnType = type;
|
||||
this.nameProperty = nameProperty;
|
||||
this.isOptional = isOptional;
|
||||
this.useDefaultForNull = useDefaultForNull;
|
||||
}
|
||||
|
||||
public Property<String> getNameProperty() {
|
||||
return nameProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String resolveName(ColumnContext columnContext) {
|
||||
return columnContext.getName(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnType<T> getType() {
|
||||
return columnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isColumnUsed(ColumnContext columnContext) {
|
||||
return !isOptional || !resolveName(columnContext).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useDefaultForNullValue(ColumnContext columnContext) {
|
||||
return useDefaultForNull && columnContext.hasDefaultSupport();
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package fr.xephi.authme.datasource.columnshandler;
|
||||
|
||||
import ch.jalu.configme.properties.Property;
|
||||
import ch.jalu.datasourcecolumns.ColumnType;
|
||||
import ch.jalu.datasourcecolumns.DependentColumn;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Implementation for columns which can also be retrieved from a {@link PlayerAuth} object.
|
||||
*
|
||||
* @param <T> column type
|
||||
*/
|
||||
public class PlayerAuthColumn<T> extends DataSourceColumn<T> implements DependentColumn<T, ColumnContext, PlayerAuth> {
|
||||
|
||||
private final Function<PlayerAuth, T> playerAuthGetter;
|
||||
|
||||
/*
|
||||
* Constructor. See parent class for details.
|
||||
*/
|
||||
PlayerAuthColumn(ColumnType<T> type, Property<String> nameProperty, boolean isOptional, boolean useDefaultForNull,
|
||||
Function<PlayerAuth, T> playerAuthGetter) {
|
||||
super(type, nameProperty, isOptional, useDefaultForNull);
|
||||
this.playerAuthGetter = playerAuthGetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getValueFromDependent(PlayerAuth auth) {
|
||||
return playerAuthGetter.apply(auth);
|
||||
}
|
||||
}
|
@ -85,6 +85,7 @@ class XfBcryptExtension extends MySqlExtension {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void extendAuth(PlayerAuth auth, int id, Connection con) throws SQLException {
|
||||
try (PreparedStatement pst = con.prepareStatement(
|
||||
"SELECT data FROM " + xfPrefix + "user_authenticate WHERE " + col.ID + "=?;")) {
|
||||
|
@ -0,0 +1,70 @@
|
||||
package fr.xephi.authme.events;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
/**
|
||||
* This event is called when a player uses the register command,
|
||||
* it's fired even when a user does a /register with invalid arguments.
|
||||
* {@link #setCanRegister(boolean) event.setCanRegister(false)} prevents the player from registering.
|
||||
*/
|
||||
public class AuthMeAsyncPreRegisterEvent extends CustomEvent {
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private final Player player;
|
||||
private boolean canRegister = true;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param player The player
|
||||
* @param isAsync True if the event is async, false otherwise
|
||||
*/
|
||||
public AuthMeAsyncPreRegisterEvent(Player player, boolean isAsync) {
|
||||
super(isAsync);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the player concerned by this event.
|
||||
*
|
||||
* @return The player who executed a valid {@code /login} command
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the player is allowed to register.
|
||||
*
|
||||
* @return True if the player can log in, false otherwise
|
||||
*/
|
||||
public boolean canRegister() {
|
||||
return canRegister;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define whether or not the player may register.
|
||||
*
|
||||
* @param canRegister True to allow the player to log in; false to prevent him
|
||||
*/
|
||||
public void setCanRegister(boolean canRegister) {
|
||||
this.canRegister = canRegister;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of handlers, equivalent to {@link #getHandlers()} and required by {@link Event}.
|
||||
*
|
||||
* @return The list of handlers
|
||||
*/
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
}
|
87
src/main/java/fr/xephi/authme/events/EmailChangedEvent.java
Normal file
87
src/main/java/fr/xephi/authme/events/EmailChangedEvent.java
Normal file
@ -0,0 +1,87 @@
|
||||
package fr.xephi.authme.events;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* This event is called when a player adds or changes his email address.
|
||||
*/
|
||||
public class EmailChangedEvent extends CustomEvent implements Cancellable {
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private final Player player;
|
||||
private final String oldEmail;
|
||||
private final String newEmail;
|
||||
private boolean isCancelled;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param player The player that changed email
|
||||
* @param oldEmail Old email player had on file. Can be null when user adds an email
|
||||
* @param newEmail New email that player tries to set. In case of adding email, this will contain
|
||||
* the email is trying to set.
|
||||
* @param isAsync should this event be called asynchronously?
|
||||
*/
|
||||
public EmailChangedEvent(Player player, @Nullable String oldEmail, String newEmail, boolean isAsync) {
|
||||
super(isAsync);
|
||||
this.player = player;
|
||||
this.oldEmail = oldEmail;
|
||||
this.newEmail = newEmail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return isCancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player who changes the email
|
||||
*
|
||||
* @return The player who changed the email
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the old email in case user tries to change existing email.
|
||||
*
|
||||
* @return old email stored on file. Can be null when user never had an email and adds a new one.
|
||||
*/
|
||||
public @Nullable String getOldEmail() {
|
||||
return this.oldEmail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the new email.
|
||||
*
|
||||
* @return the email user is trying to set. If user adds email and never had one before,
|
||||
* this is where such email can be found.
|
||||
*/
|
||||
public String getNewEmail() {
|
||||
return this.newEmail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.isCancelled = cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of handlers, equivalent to {@link #getHandlers()} and required by {@link Event}.
|
||||
*
|
||||
* @return The list of handlers
|
||||
*/
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
47
src/main/java/fr/xephi/authme/events/RegisterEvent.java
Normal file
47
src/main/java/fr/xephi/authme/events/RegisterEvent.java
Normal file
@ -0,0 +1,47 @@
|
||||
package fr.xephi.authme.events;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
/**
|
||||
* Event fired when a player has successfully registered.
|
||||
*/
|
||||
public class RegisterEvent extends CustomEvent {
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private final Player player;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param player The player
|
||||
*/
|
||||
public RegisterEvent(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the player that has successfully logged in or registered.
|
||||
*
|
||||
* @return The player
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of handlers, equivalent to {@link #getHandlers()} and required by {@link Event}.
|
||||
*
|
||||
* @return The list of handlers
|
||||
*/
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
}
|
@ -83,6 +83,10 @@ public class OnStartupTasks {
|
||||
logger.addFilter(new Log4JFilter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a task that regularly reminds players without a defined email to set their email,
|
||||
* if enabled.
|
||||
*/
|
||||
public void scheduleRecallEmailTask() {
|
||||
if (!settings.getProperty(RECALL_PLAYERS)) {
|
||||
return;
|
||||
|
@ -2,7 +2,7 @@ package fr.xephi.authme.initialization;
|
||||
|
||||
import ch.jalu.configme.configurationdata.ConfigurationData;
|
||||
import ch.jalu.configme.resource.PropertyResource;
|
||||
import ch.jalu.configme.resource.YamlFileResource;
|
||||
import fr.xephi.authme.service.yaml.YamlFileResourceProvider;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.SettingsMigrationService;
|
||||
import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever;
|
||||
@ -37,7 +37,7 @@ public class SettingsProvider implements Provider<Settings> {
|
||||
if (!configFile.exists()) {
|
||||
FileUtils.create(configFile);
|
||||
}
|
||||
PropertyResource resource = new YamlFileResource(configFile);
|
||||
PropertyResource resource = YamlFileResourceProvider.loadFromFile(configFile);
|
||||
ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData();
|
||||
return new Settings(dataFolder, resource, migrationService, configurationData);
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ public class OnJoinVerifier implements Reloadable {
|
||||
return false;
|
||||
} else if (!permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)) {
|
||||
// Server is full and player is NOT VIP; set kick message and proceed with kick
|
||||
event.setKickMessage(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER));
|
||||
event.setKickMessage(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -135,12 +135,12 @@ public class OnJoinVerifier implements Reloadable {
|
||||
}
|
||||
Player nonVipPlayer = generateKickPlayer(onlinePlayers);
|
||||
if (nonVipPlayer != null) {
|
||||
nonVipPlayer.kickPlayer(messages.retrieveSingle(MessageKey.KICK_FOR_VIP));
|
||||
nonVipPlayer.kickPlayer(messages.retrieveSingle(player, MessageKey.KICK_FOR_VIP));
|
||||
event.allow();
|
||||
return false;
|
||||
} else {
|
||||
ConsoleLogger.info("VIP player " + player.getName() + " tried to join, but the server was full");
|
||||
event.setKickMessage(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER));
|
||||
event.setKickMessage(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
package fr.xephi.authme.listener;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.data.QuickCommandsProtectionManager;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.permission.PlayerStatePermission;
|
||||
import fr.xephi.authme.permission.handlers.PermissionLoadUserException;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.service.AntiBotService;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
@ -50,6 +54,8 @@ import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.event.player.PlayerShearEntityEvent;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOWED_MOVEMENT_RADIUS;
|
||||
import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT;
|
||||
@ -62,7 +68,7 @@ public class PlayerListener implements Listener {
|
||||
@Inject
|
||||
private Settings settings;
|
||||
@Inject
|
||||
private Messages m;
|
||||
private Messages messages;
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
@Inject
|
||||
@ -86,9 +92,12 @@ public class PlayerListener implements Listener {
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
@Inject
|
||||
private QuickCommandsProtectionManager quickCommandsProtectionManager;
|
||||
@Inject
|
||||
private BungeeSender bungeeSender;
|
||||
|
||||
private boolean isAsyncPlayerPreLoginEventCalled = false;
|
||||
private List<String> unresolvedPlayerHostname = new ArrayList<>();
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
|
||||
@ -100,9 +109,14 @@ public class PlayerListener implements Listener {
|
||||
return;
|
||||
}
|
||||
final Player player = event.getPlayer();
|
||||
if (!quickCommandsProtectionManager.isAllowed(player.getName())) {
|
||||
event.setCancelled(true);
|
||||
player.kickPlayer(messages.retrieveSingle(player, MessageKey.QUICK_COMMAND_PROTECTION_KICK));
|
||||
return;
|
||||
}
|
||||
if (listenerService.shouldCancelEvent(player)) {
|
||||
event.setCancelled(true);
|
||||
m.send(player, MessageKey.DENIED_COMMAND);
|
||||
messages.send(player, MessageKey.DENIED_COMMAND);
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,10 +127,18 @@ public class PlayerListener implements Listener {
|
||||
}
|
||||
|
||||
final Player player = event.getPlayer();
|
||||
if (listenerService.shouldCancelEvent(player)) {
|
||||
final boolean mayPlayerSendChat = !listenerService.shouldCancelEvent(player)
|
||||
|| permissionsManager.hasPermission(player, PlayerStatePermission.ALLOW_CHAT_BEFORE_LOGIN);
|
||||
if (mayPlayerSendChat) {
|
||||
removeUnauthorizedRecipients(event);
|
||||
} else {
|
||||
event.setCancelled(true);
|
||||
m.send(player, MessageKey.DENIED_CHAT);
|
||||
} else if (settings.getProperty(RestrictionSettings.HIDE_CHAT)) {
|
||||
messages.send(player, MessageKey.DENIED_CHAT);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeUnauthorizedRecipients(AsyncPlayerChatEvent event) {
|
||||
if (settings.getProperty(RestrictionSettings.HIDE_CHAT)) {
|
||||
event.getRecipients().removeIf(listenerService::shouldCancelEvent);
|
||||
if (event.getRecipients().isEmpty()) {
|
||||
event.setCancelled(true);
|
||||
@ -178,6 +200,7 @@ public class PlayerListener implements Listener {
|
||||
|
||||
String customJoinMessage = settings.getProperty(RegistrationSettings.CUSTOM_JOIN_MESSAGE);
|
||||
if (!customJoinMessage.isEmpty()) {
|
||||
customJoinMessage = ChatColor.translateAlternateColorCodes('&', customJoinMessage);
|
||||
event.setJoinMessage(customJoinMessage
|
||||
.replace("{PLAYERNAME}", player.getName())
|
||||
.replace("{DISPLAYNAME}", player.getDisplayName())
|
||||
@ -206,6 +229,9 @@ public class PlayerListener implements Listener {
|
||||
if (!PlayerListener19Spigot.isPlayerSpawnLocationEventCalled()) {
|
||||
teleportationService.teleportOnJoin(player);
|
||||
}
|
||||
|
||||
quickCommandsProtectionManager.processJoin(player);
|
||||
|
||||
management.performJoin(player);
|
||||
|
||||
teleportationService.teleportNewPlayerToFirstSpawn(player);
|
||||
@ -240,6 +266,11 @@ public class PlayerListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getAddress() == null) {
|
||||
unresolvedPlayerHostname.add(event.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
final String name = event.getName();
|
||||
|
||||
if (validationService.isUnrestricted(name)) {
|
||||
@ -248,15 +279,19 @@ public class PlayerListener implements Listener {
|
||||
|
||||
// Keep pre-UUID compatibility
|
||||
try {
|
||||
permissionsManager.loadUserData(event.getUniqueId());
|
||||
} catch (NoSuchMethodError e) {
|
||||
permissionsManager.loadUserData(name);
|
||||
try {
|
||||
permissionsManager.loadUserData(event.getUniqueId());
|
||||
} catch (NoSuchMethodError e) {
|
||||
permissionsManager.loadUserData(name);
|
||||
}
|
||||
} catch (PermissionLoadUserException e) {
|
||||
ConsoleLogger.logException("Unable to load the permission data of user " + name, e);
|
||||
}
|
||||
|
||||
try {
|
||||
runOnJoinChecks(JoiningPlayer.fromName(name), event.getAddress().getHostAddress());
|
||||
} catch (FailedVerificationException e) {
|
||||
event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs()));
|
||||
event.setKickMessage(messages.retrieveSingle(name, e.getReason(), e.getArgs()));
|
||||
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
|
||||
}
|
||||
}
|
||||
@ -280,11 +315,13 @@ public class PlayerListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isAsyncPlayerPreLoginEventCalled || !settings.getProperty(PluginSettings.USE_ASYNC_PRE_LOGIN_EVENT)) {
|
||||
|
||||
if (!isAsyncPlayerPreLoginEventCalled || !settings.getProperty(PluginSettings.USE_ASYNC_PRE_LOGIN_EVENT)
|
||||
|| unresolvedPlayerHostname.remove(name)) {
|
||||
try {
|
||||
runOnJoinChecks(JoiningPlayer.fromPlayerObject(player), event.getAddress().getHostAddress());
|
||||
} catch (FailedVerificationException e) {
|
||||
event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs()));
|
||||
event.setKickMessage(messages.retrieveSingle(player, e.getReason(), e.getArgs()));
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_OTHER);
|
||||
}
|
||||
}
|
||||
|
@ -12,10 +12,11 @@ import javax.inject.Inject;
|
||||
|
||||
public class PlayerListener19Spigot implements Listener {
|
||||
|
||||
private static boolean isPlayerSpawnLocationEventCalled = false;
|
||||
|
||||
@Inject
|
||||
private TeleportationService teleportationService;
|
||||
|
||||
private static boolean isPlayerSpawnLocationEventCalled = false;
|
||||
|
||||
public static boolean isPlayerSpawnLocationEventCalled() {
|
||||
return isPlayerSpawnLocationEventCalled;
|
||||
|
@ -5,9 +5,9 @@ import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.RestrictionSettings;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@ -46,7 +46,7 @@ public class ProtocolLibService implements SettingsDependent {
|
||||
if (protectInvBeforeLogin) {
|
||||
ConsoleLogger.warning("WARNING! The protectInventory feature requires ProtocolLib! Disabling it...");
|
||||
}
|
||||
|
||||
|
||||
if (denyTabCompleteBeforeLogin) {
|
||||
ConsoleLogger.warning("WARNING! The denyTabComplete feature requires ProtocolLib! Disabling it...");
|
||||
}
|
||||
@ -56,16 +56,21 @@ public class ProtocolLibService implements SettingsDependent {
|
||||
}
|
||||
|
||||
// Set up packet adapters
|
||||
if (protectInvBeforeLogin && inventoryPacketAdapter == null) {
|
||||
inventoryPacketAdapter = new InventoryPacketAdapter(plugin, playerCache);
|
||||
inventoryPacketAdapter.register();
|
||||
if (protectInvBeforeLogin) {
|
||||
if (inventoryPacketAdapter == null) {
|
||||
inventoryPacketAdapter = new InventoryPacketAdapter(plugin, playerCache);
|
||||
inventoryPacketAdapter.register();
|
||||
}
|
||||
} else if (inventoryPacketAdapter != null) {
|
||||
inventoryPacketAdapter.unregister();
|
||||
inventoryPacketAdapter = null;
|
||||
}
|
||||
if (denyTabCompleteBeforeLogin && tabCompletePacketAdapter == null) {
|
||||
tabCompletePacketAdapter = new TabCompletePacketAdapter(plugin, playerCache);
|
||||
tabCompletePacketAdapter.register();
|
||||
|
||||
if (denyTabCompleteBeforeLogin) {
|
||||
if (tabCompletePacketAdapter == null) {
|
||||
tabCompletePacketAdapter = new TabCompletePacketAdapter(plugin, playerCache);
|
||||
tabCompletePacketAdapter.register();
|
||||
}
|
||||
} else if (tabCompletePacketAdapter != null) {
|
||||
tabCompletePacketAdapter.unregister();
|
||||
tabCompletePacketAdapter = null;
|
||||
@ -106,7 +111,7 @@ public class ProtocolLibService implements SettingsDependent {
|
||||
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) {
|
||||
if (oldProtectInventory && !protectInvBeforeLogin && inventoryPacketAdapter != null) {
|
||||
inventoryPacketAdapter.unregister();
|
||||
for (Player onlinePlayer : bukkitService.getOnlinePlayers()) {
|
||||
if (!playerCache.isAuthenticated(onlinePlayer.getName())) {
|
||||
|
@ -0,0 +1,124 @@
|
||||
package fr.xephi.authme.message;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.initialization.DataFolder;
|
||||
import fr.xephi.authme.initialization.Reloadable;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.PluginSettings;
|
||||
import fr.xephi.authme.util.FileUtils;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Handles a YAML message file with a default file fallback.
|
||||
*/
|
||||
public abstract class AbstractMessageFileHandler implements Reloadable {
|
||||
|
||||
protected static final String DEFAULT_LANGUAGE = "en";
|
||||
|
||||
@DataFolder
|
||||
@Inject
|
||||
private File dataFolder;
|
||||
|
||||
@Inject
|
||||
private Settings settings;
|
||||
|
||||
private String filename;
|
||||
private FileConfiguration configuration;
|
||||
private final String defaultFile;
|
||||
|
||||
protected AbstractMessageFileHandler() {
|
||||
this.defaultFile = createFilePath(DEFAULT_LANGUAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostConstruct
|
||||
public void reload() {
|
||||
String language = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE);
|
||||
filename = createFilePath(language);
|
||||
File messagesFile = initializeFile(filename);
|
||||
configuration = YamlConfiguration.loadConfiguration(messagesFile);
|
||||
}
|
||||
|
||||
protected String getLanguage() {
|
||||
return settings.getProperty(PluginSettings.MESSAGES_LANGUAGE);
|
||||
}
|
||||
|
||||
protected File getUserLanguageFile() {
|
||||
return new File(dataFolder, filename);
|
||||
}
|
||||
|
||||
protected String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the message file configuration has an entry at the given path.
|
||||
*
|
||||
* @param path the path to verify
|
||||
* @return true if an entry exists for the path in the messages file, false otherwise
|
||||
*/
|
||||
public boolean hasSection(String path) {
|
||||
return configuration.get(path) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message for the given key.
|
||||
*
|
||||
* @param key the key to retrieve the message for
|
||||
* @return the message
|
||||
*/
|
||||
public String getMessage(String key) {
|
||||
String message = configuration.getString(key);
|
||||
return message == null
|
||||
? "Error retrieving message '" + key + "'"
|
||||
: message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message for the given key only if it exists,
|
||||
* i.e. without falling back to the default file.
|
||||
*
|
||||
* @param key the key to retrieve the message for
|
||||
* @return the message, or {@code null} if not available
|
||||
*/
|
||||
public String getMessageIfExists(String key) {
|
||||
return configuration.getString(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the path to the messages file for the given language code.
|
||||
*
|
||||
* @param language the language code
|
||||
* @return path to the message file for the given language
|
||||
*/
|
||||
protected abstract String createFilePath(String language);
|
||||
|
||||
/**
|
||||
* Copies the messages file from the JAR to the local messages/ folder if it doesn't exist.
|
||||
*
|
||||
* @param filePath path to the messages file to use
|
||||
* @return the messages file to use
|
||||
*/
|
||||
@VisibleForTesting
|
||||
File initializeFile(String filePath) {
|
||||
File file = new File(dataFolder, filePath);
|
||||
// Check that JAR file exists to avoid logging an error
|
||||
if (FileUtils.getResourceFromJar(filePath) != null && FileUtils.copyFileFromResource(file, filePath)) {
|
||||
return file;
|
||||
}
|
||||
|
||||
if (FileUtils.copyFileFromResource(file, defaultFile)) {
|
||||
return file;
|
||||
} else {
|
||||
ConsoleLogger.warning("Wanted to copy default messages file '" + defaultFile
|
||||
+ "' from JAR but it didn't exist");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package fr.xephi.authme.message;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.util.FileUtils;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* File handler for the help_xx.yml resource.
|
||||
*/
|
||||
public class HelpMessagesFileHandler extends AbstractMessageFileHandler {
|
||||
|
||||
private FileConfiguration defaultConfiguration;
|
||||
|
||||
@Inject // Trigger injection in the superclass
|
||||
HelpMessagesFileHandler() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message for the given key.
|
||||
*
|
||||
* @param key the key to retrieve the message for
|
||||
* @return the message
|
||||
*/
|
||||
@Override
|
||||
public String getMessage(String key) {
|
||||
String message = getMessageIfExists(key);
|
||||
|
||||
if (message == null) {
|
||||
ConsoleLogger.warning("Error getting message with key '" + key + "'. "
|
||||
+ "Please update your config file '" + getFilename() + "' or run /authme messages help");
|
||||
return getDefault(key);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message from the default file.
|
||||
*
|
||||
* @param key the key to retrieve the message for
|
||||
* @return the message from the default file
|
||||
*/
|
||||
private String getDefault(String key) {
|
||||
if (defaultConfiguration == null) {
|
||||
InputStream stream = FileUtils.getResourceFromJar(createFilePath(DEFAULT_LANGUAGE));
|
||||
defaultConfiguration = YamlConfiguration.loadConfiguration(new InputStreamReader(stream));
|
||||
}
|
||||
String message = defaultConfiguration.getString(key);
|
||||
return message == null
|
||||
? "Error retrieving message '" + key + "'"
|
||||
: message;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String createFilePath(String language) {
|
||||
return "messages/help_" + language + ".yml";
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
package fr.xephi.authme.message;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.util.FileUtils;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* Handles a YAML message file with a default file fallback.
|
||||
*/
|
||||
public class MessageFileHandler {
|
||||
|
||||
// regular file
|
||||
private final String filename;
|
||||
private final FileConfiguration configuration;
|
||||
private final String updateAddition;
|
||||
// default file
|
||||
private final String defaultFile;
|
||||
private FileConfiguration defaultConfiguration;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param file the file to use for messages
|
||||
* @param defaultFile the default file from the JAR to use if no message is found
|
||||
* @param updateCommand command to update the messages file (nullable) to show in error messages
|
||||
*/
|
||||
public MessageFileHandler(File file, String defaultFile, String updateCommand) {
|
||||
this.filename = file.getName();
|
||||
this.configuration = YamlConfiguration.loadConfiguration(file);
|
||||
this.defaultFile = defaultFile;
|
||||
this.updateAddition = updateCommand == null
|
||||
? ""
|
||||
: " (or run " + updateCommand + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the message file configuration has an entry at the given path.
|
||||
*
|
||||
* @param path the path to verify
|
||||
* @return true if an entry exists for the path in the messages file, false otherwise
|
||||
*/
|
||||
public boolean hasSection(String path) {
|
||||
return configuration.get(path) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message for the given key.
|
||||
*
|
||||
* @param key the key to retrieve the message for
|
||||
* @return the message
|
||||
*/
|
||||
public String getMessage(String key) {
|
||||
String message = configuration.getString(key);
|
||||
|
||||
if (message == null) {
|
||||
ConsoleLogger.warning("Error getting message with key '" + key + "'. "
|
||||
+ "Please update your config file '" + filename + "'" + updateAddition);
|
||||
return getDefault(key);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message for the given key only if it exists,
|
||||
* i.e. without falling back to the default file.
|
||||
*
|
||||
* @param key the key to retrieve the message for
|
||||
* @return the message, or {@code null} if not available
|
||||
*/
|
||||
public String getMessageIfExists(String key) {
|
||||
return configuration.getString(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message from the default file.
|
||||
*
|
||||
* @param key the key to retrieve the message for
|
||||
* @return the message from the default file
|
||||
*/
|
||||
private String getDefault(String key) {
|
||||
if (defaultConfiguration == null) {
|
||||
InputStream stream = FileUtils.getResourceFromJar(defaultFile);
|
||||
defaultConfiguration = YamlConfiguration.loadConfiguration(new InputStreamReader(stream));
|
||||
}
|
||||
String message = defaultConfiguration.getString(key);
|
||||
return message == null
|
||||
? "Error retrieving message '" + key + "'"
|
||||
: message;
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
package fr.xephi.authme.message;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import fr.xephi.authme.initialization.DataFolder;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.PluginSettings;
|
||||
import fr.xephi.authme.util.FileUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Injectable creator of {@link MessageFileHandler} instances.
|
||||
*
|
||||
* @see MessageFileHandler
|
||||
*/
|
||||
public class MessageFileHandlerProvider {
|
||||
|
||||
private static final String DEFAULT_LANGUAGE = "en";
|
||||
|
||||
@Inject
|
||||
@DataFolder
|
||||
private File dataFolder;
|
||||
@Inject
|
||||
private Settings settings;
|
||||
|
||||
MessageFileHandlerProvider() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a message file handler with the messages file of the configured language.
|
||||
* Ensures beforehand that the messages file exists or creates it otherwise.
|
||||
*
|
||||
* @param pathBuilder function taking the configured language code as argument and returning the messages file
|
||||
* @return the message file handler
|
||||
*/
|
||||
public MessageFileHandler initializeHandler(Function<String, String> pathBuilder) {
|
||||
return initializeHandler(pathBuilder, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a message file handler with the messages file of the configured language.
|
||||
* Ensures beforehand that the messages file exists or creates it otherwise.
|
||||
*
|
||||
* @param pathBuilder function taking the configured language code as argument and returning the messages file
|
||||
* @param updateCommand command to run to update the languages file (nullable)
|
||||
* @return the message file handler
|
||||
*/
|
||||
public MessageFileHandler initializeHandler(Function<String, String> pathBuilder, String updateCommand) {
|
||||
String language = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE);
|
||||
return new MessageFileHandler(
|
||||
initializeFile(language, pathBuilder),
|
||||
pathBuilder.apply(DEFAULT_LANGUAGE),
|
||||
updateCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the messages file from the JAR if it doesn't exist.
|
||||
*
|
||||
* @param language the configured language code
|
||||
* @param pathBuilder function returning message file name with language as argument
|
||||
* @return the messages file to use
|
||||
*/
|
||||
@VisibleForTesting
|
||||
File initializeFile(String language, Function<String, String> pathBuilder) {
|
||||
String filePath = pathBuilder.apply(language);
|
||||
File file = new File(dataFolder, filePath);
|
||||
// Check that JAR file exists to avoid logging an error
|
||||
if (FileUtils.getResourceFromJar(filePath) != null && FileUtils.copyFileFromResource(file, filePath)) {
|
||||
return file;
|
||||
}
|
||||
|
||||
String defaultFilePath = pathBuilder.apply(DEFAULT_LANGUAGE);
|
||||
if (FileUtils.copyFileFromResource(file, defaultFilePath)) {
|
||||
return file;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -6,298 +6,328 @@ package fr.xephi.authme.message;
|
||||
public enum MessageKey {
|
||||
|
||||
/** In order to use this command you must be authenticated! */
|
||||
DENIED_COMMAND("denied_command"),
|
||||
DENIED_COMMAND("error.denied_command"),
|
||||
|
||||
/** A player with the same IP is already in game! */
|
||||
SAME_IP_ONLINE("same_ip_online"),
|
||||
SAME_IP_ONLINE("on_join_validation.same_ip_online"),
|
||||
|
||||
/** In order to chat you must be authenticated! */
|
||||
DENIED_CHAT("denied_chat"),
|
||||
DENIED_CHAT("error.denied_chat"),
|
||||
|
||||
/** AntiBot protection mode is enabled! You have to wait some minutes before joining the server. */
|
||||
KICK_ANTIBOT("kick_antibot"),
|
||||
KICK_ANTIBOT("antibot.kick_antibot"),
|
||||
|
||||
/** This user isn't registered! */
|
||||
UNKNOWN_USER("unknown_user"),
|
||||
UNKNOWN_USER("error.unregistered_user"),
|
||||
|
||||
/** You're not logged in! */
|
||||
NOT_LOGGED_IN("not_logged_in"),
|
||||
NOT_LOGGED_IN("error.not_logged_in"),
|
||||
|
||||
/** Usage: /login <password> */
|
||||
USAGE_LOGIN("usage_log"),
|
||||
USAGE_LOGIN("login.command_usage"),
|
||||
|
||||
/** Wrong password! */
|
||||
WRONG_PASSWORD("wrong_pwd"),
|
||||
WRONG_PASSWORD("login.wrong_password"),
|
||||
|
||||
/** Successfully unregistered! */
|
||||
UNREGISTERED_SUCCESS("unregistered"),
|
||||
UNREGISTERED_SUCCESS("unregister.success"),
|
||||
|
||||
/** In-game registration is disabled! */
|
||||
REGISTRATION_DISABLED("reg_disabled"),
|
||||
REGISTRATION_DISABLED("registration.disabled"),
|
||||
|
||||
/** Logged-in due to Session Reconnection. */
|
||||
SESSION_RECONNECTION("valid_session"),
|
||||
SESSION_RECONNECTION("session.valid_session"),
|
||||
|
||||
/** Successful login! */
|
||||
LOGIN_SUCCESS("login"),
|
||||
LOGIN_SUCCESS("login.success"),
|
||||
|
||||
/** Your account isn't activated yet, please check your emails! */
|
||||
ACCOUNT_NOT_ACTIVATED("vb_nonActiv"),
|
||||
ACCOUNT_NOT_ACTIVATED("misc.account_not_activated"),
|
||||
|
||||
/** You already have registered this username! */
|
||||
NAME_ALREADY_REGISTERED("user_regged"),
|
||||
NAME_ALREADY_REGISTERED("registration.name_taken"),
|
||||
|
||||
/** You don't have the permission to perform this action! */
|
||||
NO_PERMISSION("no_perm"),
|
||||
NO_PERMISSION("error.no_permission"),
|
||||
|
||||
/** An unexpected error occurred, please contact an administrator! */
|
||||
ERROR("error"),
|
||||
ERROR("error.unexpected_error"),
|
||||
|
||||
/** Please, login with the command: /login <password> */
|
||||
LOGIN_MESSAGE("login_msg"),
|
||||
LOGIN_MESSAGE("login.login_request"),
|
||||
|
||||
/** Please, register to the server with the command: /register <password> <ConfirmPassword> */
|
||||
REGISTER_MESSAGE("reg_msg"),
|
||||
REGISTER_MESSAGE("registration.register_request"),
|
||||
|
||||
/** You have exceeded the maximum number of registrations (%reg_count/%max_acc %reg_names) for your connection! */
|
||||
MAX_REGISTER_EXCEEDED("max_reg", "%max_acc", "%reg_count", "%reg_names"),
|
||||
MAX_REGISTER_EXCEEDED("error.max_registration", "%max_acc", "%reg_count", "%reg_names"),
|
||||
|
||||
/** Usage: /register <password> <ConfirmPassword> */
|
||||
USAGE_REGISTER("usage_reg"),
|
||||
USAGE_REGISTER("registration.command_usage"),
|
||||
|
||||
/** Usage: /unregister <password> */
|
||||
USAGE_UNREGISTER("usage_unreg"),
|
||||
USAGE_UNREGISTER("unregister.command_usage"),
|
||||
|
||||
/** Password changed successfully! */
|
||||
PASSWORD_CHANGED_SUCCESS("pwd_changed"),
|
||||
PASSWORD_CHANGED_SUCCESS("misc.password_changed"),
|
||||
|
||||
/** Passwords didn't match, check them again! */
|
||||
PASSWORD_MATCH_ERROR("password_error"),
|
||||
PASSWORD_MATCH_ERROR("password.match_error"),
|
||||
|
||||
/** You can't use your name as password, please choose another one... */
|
||||
PASSWORD_IS_USERNAME_ERROR("password_error_nick"),
|
||||
PASSWORD_IS_USERNAME_ERROR("password.name_in_password"),
|
||||
|
||||
/** The chosen password isn't safe, please choose another one... */
|
||||
PASSWORD_UNSAFE_ERROR("password_error_unsafe"),
|
||||
PASSWORD_UNSAFE_ERROR("password.unsafe_password"),
|
||||
|
||||
/** Your password contains illegal characters. Allowed chars: REG_EX */
|
||||
PASSWORD_CHARACTERS_ERROR("password_error_chars", "REG_EX"),
|
||||
/** Your password contains illegal characters. Allowed chars: %valid_chars */
|
||||
PASSWORD_CHARACTERS_ERROR("password.forbidden_characters", "%valid_chars"),
|
||||
|
||||
/** Your IP has been changed and your session data has expired! */
|
||||
SESSION_EXPIRED("invalid_session"),
|
||||
SESSION_EXPIRED("session.invalid_session"),
|
||||
|
||||
/** Only registered users can join the server! Please visit http://example.com to register yourself! */
|
||||
MUST_REGISTER_MESSAGE("reg_only"),
|
||||
MUST_REGISTER_MESSAGE("registration.reg_only"),
|
||||
|
||||
/** You're already logged in! */
|
||||
ALREADY_LOGGED_IN_ERROR("logged_in"),
|
||||
ALREADY_LOGGED_IN_ERROR("error.logged_in"),
|
||||
|
||||
/** Logged out successfully! */
|
||||
LOGOUT_SUCCESS("logout"),
|
||||
LOGOUT_SUCCESS("misc.logout"),
|
||||
|
||||
/** The same username is already playing on the server! */
|
||||
USERNAME_ALREADY_ONLINE_ERROR("same_nick"),
|
||||
USERNAME_ALREADY_ONLINE_ERROR("on_join_validation.same_nick_online"),
|
||||
|
||||
/** Successfully registered! */
|
||||
REGISTER_SUCCESS("registered"),
|
||||
REGISTER_SUCCESS("registration.success"),
|
||||
|
||||
/** Your password is too short or too long! Please try with another one! */
|
||||
INVALID_PASSWORD_LENGTH("pass_len"),
|
||||
INVALID_PASSWORD_LENGTH("password.wrong_length"),
|
||||
|
||||
/** Configuration and database have been reloaded correctly! */
|
||||
CONFIG_RELOAD_SUCCESS("reload"),
|
||||
CONFIG_RELOAD_SUCCESS("misc.reload"),
|
||||
|
||||
/** Login timeout exceeded, you have been kicked from the server, please try again! */
|
||||
LOGIN_TIMEOUT_ERROR("timeout"),
|
||||
LOGIN_TIMEOUT_ERROR("login.timeout_error"),
|
||||
|
||||
/** Usage: /changepassword <oldPassword> <newPassword> */
|
||||
USAGE_CHANGE_PASSWORD("usage_changepassword"),
|
||||
USAGE_CHANGE_PASSWORD("misc.usage_change_password"),
|
||||
|
||||
/** Your username is either too short or too long! */
|
||||
INVALID_NAME_LENGTH("name_len"),
|
||||
INVALID_NAME_LENGTH("on_join_validation.name_length"),
|
||||
|
||||
/** Your username contains illegal characters. Allowed chars: REG_EX */
|
||||
INVALID_NAME_CHARACTERS("regex", "REG_EX"),
|
||||
/** Your username contains illegal characters. Allowed chars: %valid_chars */
|
||||
INVALID_NAME_CHARACTERS("on_join_validation.characters_in_name", "%valid_chars"),
|
||||
|
||||
/** Please add your email to your account with the command: /email add <yourEmail> <confirmEmail> */
|
||||
ADD_EMAIL_MESSAGE("add_email"),
|
||||
ADD_EMAIL_MESSAGE("email.add_email_request"),
|
||||
|
||||
/** Forgot your password? Please use the command: /email recovery <yourEmail> */
|
||||
FORGOT_PASSWORD_MESSAGE("recovery_email"),
|
||||
FORGOT_PASSWORD_MESSAGE("recovery.forgot_password_hint"),
|
||||
|
||||
/** To login you have to solve a captcha code, please use the command: /captcha <theCaptcha> */
|
||||
USAGE_CAPTCHA("usage_captcha", "<theCaptcha>"),
|
||||
/** To log in you have to solve a captcha code, please use the command: /captcha %captcha_code */
|
||||
USAGE_CAPTCHA("captcha.usage_captcha", "%captcha_code"),
|
||||
|
||||
/** Wrong captcha, please type "/captcha THE_CAPTCHA" into the chat! */
|
||||
CAPTCHA_WRONG_ERROR("wrong_captcha", "THE_CAPTCHA"),
|
||||
/** Wrong captcha, please type "/captcha %captcha_code" into the chat! */
|
||||
CAPTCHA_WRONG_ERROR("captcha.wrong_captcha", "%captcha_code"),
|
||||
|
||||
/** Captcha code solved correctly! */
|
||||
CAPTCHA_SUCCESS("valid_captcha"),
|
||||
CAPTCHA_SUCCESS("captcha.valid_captcha"),
|
||||
|
||||
/** To register you have to solve a captcha code first, please use the command: /captcha <theCaptcha> */
|
||||
CAPTCHA_FOR_REGISTRATION_REQUIRED("captcha_for_registration", "<theCaptcha>"),
|
||||
/** To register you have to solve a captcha first, please use the command: /captcha %captcha_code */
|
||||
CAPTCHA_FOR_REGISTRATION_REQUIRED("captcha.captcha_for_registration", "%captcha_code"),
|
||||
|
||||
/** Valid captcha! You may now register with /register */
|
||||
REGISTER_CAPTCHA_SUCCESS("register_captcha_valid"),
|
||||
REGISTER_CAPTCHA_SUCCESS("captcha.register_captcha_valid"),
|
||||
|
||||
/** A VIP player has joined the server when it was full! */
|
||||
KICK_FOR_VIP("kick_forvip"),
|
||||
KICK_FOR_VIP("error.kick_for_vip"),
|
||||
|
||||
/** The server is full, try again later! */
|
||||
KICK_FULL_SERVER("kick_fullserver"),
|
||||
KICK_FULL_SERVER("on_join_validation.kick_full_server"),
|
||||
|
||||
/** Usage: /email add <email> <confirmEmail> */
|
||||
USAGE_ADD_EMAIL("usage_email_add"),
|
||||
USAGE_ADD_EMAIL("email.usage_email_add"),
|
||||
|
||||
/** Usage: /email change <oldEmail> <newEmail> */
|
||||
USAGE_CHANGE_EMAIL("usage_email_change"),
|
||||
USAGE_CHANGE_EMAIL("email.usage_email_change"),
|
||||
|
||||
/** Usage: /email recovery <Email> */
|
||||
USAGE_RECOVER_EMAIL("usage_email_recovery"),
|
||||
USAGE_RECOVER_EMAIL("recovery.command_usage"),
|
||||
|
||||
/** Invalid new email, try again! */
|
||||
INVALID_NEW_EMAIL("new_email_invalid"),
|
||||
INVALID_NEW_EMAIL("email.new_email_invalid"),
|
||||
|
||||
/** Invalid old email, try again! */
|
||||
INVALID_OLD_EMAIL("old_email_invalid"),
|
||||
INVALID_OLD_EMAIL("email.old_email_invalid"),
|
||||
|
||||
/** Invalid email address, try again! */
|
||||
INVALID_EMAIL("email_invalid"),
|
||||
INVALID_EMAIL("email.invalid"),
|
||||
|
||||
/** Email address successfully added to your account! */
|
||||
EMAIL_ADDED_SUCCESS("email_added"),
|
||||
EMAIL_ADDED_SUCCESS("email.added"),
|
||||
|
||||
/** Adding email was not allowed */
|
||||
EMAIL_ADD_NOT_ALLOWED("email.add_not_allowed"),
|
||||
|
||||
/** Please confirm your email address! */
|
||||
CONFIRM_EMAIL_MESSAGE("email_confirm"),
|
||||
CONFIRM_EMAIL_MESSAGE("email.request_confirmation"),
|
||||
|
||||
/** Email address changed correctly! */
|
||||
EMAIL_CHANGED_SUCCESS("email_changed"),
|
||||
EMAIL_CHANGED_SUCCESS("email.changed"),
|
||||
|
||||
/** Changing email was not allowed */
|
||||
EMAIL_CHANGE_NOT_ALLOWED("email.change_not_allowed"),
|
||||
|
||||
/** Your current email address is: %email */
|
||||
EMAIL_SHOW("email_show", "%email"),
|
||||
EMAIL_SHOW("email.email_show", "%email"),
|
||||
|
||||
/** You currently don't have email address associated with this account. */
|
||||
SHOW_NO_EMAIL("show_no_email"),
|
||||
SHOW_NO_EMAIL("email.no_email_for_account"),
|
||||
|
||||
/** Recovery email sent successfully! Please check your email inbox! */
|
||||
RECOVERY_EMAIL_SENT_MESSAGE("email_send"),
|
||||
RECOVERY_EMAIL_SENT_MESSAGE("recovery.email_sent"),
|
||||
|
||||
/** Your country is banned from this server! */
|
||||
COUNTRY_BANNED_ERROR("country_banned"),
|
||||
COUNTRY_BANNED_ERROR("on_join_validation.country_banned"),
|
||||
|
||||
/** [AntiBotService] AntiBot enabled due to the huge number of connections! */
|
||||
ANTIBOT_AUTO_ENABLED_MESSAGE("antibot_auto_enabled"),
|
||||
ANTIBOT_AUTO_ENABLED_MESSAGE("antibot.auto_enabled"),
|
||||
|
||||
/** [AntiBotService] AntiBot disabled after %m minutes! */
|
||||
ANTIBOT_AUTO_DISABLED_MESSAGE("antibot_auto_disabled", "%m"),
|
||||
ANTIBOT_AUTO_DISABLED_MESSAGE("antibot.auto_disabled", "%m"),
|
||||
|
||||
/** The email address is already being used */
|
||||
EMAIL_ALREADY_USED_ERROR("email_already_used"),
|
||||
EMAIL_ALREADY_USED_ERROR("email.already_used"),
|
||||
|
||||
/** Your secret code is %code. You can scan it from here %url */
|
||||
TWO_FACTOR_CREATE("two_factor_create", "%code", "%url"),
|
||||
TWO_FACTOR_CREATE("two_factor.code_created", "%code", "%url"),
|
||||
|
||||
/** Please confirm your code with /2fa confirm <code> */
|
||||
TWO_FACTOR_CREATE_CONFIRMATION_REQUIRED("two_factor.confirmation_required"),
|
||||
|
||||
/** Please submit your two-factor authentication code with /2fa code <code> */
|
||||
TWO_FACTOR_CODE_REQUIRED("two_factor.code_required"),
|
||||
|
||||
/** Two-factor authentication is already enabled for your account! */
|
||||
TWO_FACTOR_ALREADY_ENABLED("two_factor.already_enabled"),
|
||||
|
||||
/** No 2fa key has been generated for you or it has expired. Please run /2fa add */
|
||||
TWO_FACTOR_ENABLE_ERROR_NO_CODE("two_factor.enable_error_no_code"),
|
||||
|
||||
/** Successfully enabled two-factor authentication for your account */
|
||||
TWO_FACTOR_ENABLE_SUCCESS("two_factor.enable_success"),
|
||||
|
||||
/** Wrong code or code has expired. Please run /2fa add */
|
||||
TWO_FACTOR_ENABLE_ERROR_WRONG_CODE("two_factor.enable_error_wrong_code"),
|
||||
|
||||
/** Two-factor authentication is not enabled for your account. Run /2fa add */
|
||||
TWO_FACTOR_NOT_ENABLED_ERROR("two_factor.not_enabled_error"),
|
||||
|
||||
/** Successfully removed two-factor auth from your account */
|
||||
TWO_FACTOR_REMOVED_SUCCESS("two_factor.removed_success"),
|
||||
|
||||
/** Invalid code! */
|
||||
TWO_FACTOR_INVALID_CODE("two_factor.invalid_code"),
|
||||
|
||||
/** You are not the owner of this account. Please choose another name! */
|
||||
NOT_OWNER_ERROR("not_owner_error"),
|
||||
NOT_OWNER_ERROR("on_join_validation.not_owner_error"),
|
||||
|
||||
/** You should join using username %valid, not %invalid. */
|
||||
INVALID_NAME_CASE("invalid_name_case", "%valid", "%invalid"),
|
||||
INVALID_NAME_CASE("on_join_validation.invalid_name_case", "%valid", "%invalid"),
|
||||
|
||||
/** You have been temporarily banned for failing to log in too many times. */
|
||||
TEMPBAN_MAX_LOGINS("tempban_max_logins"),
|
||||
TEMPBAN_MAX_LOGINS("error.tempban_max_logins"),
|
||||
|
||||
/** You own %count accounts: */
|
||||
ACCOUNTS_OWNED_SELF("accounts_owned_self", "%count"),
|
||||
ACCOUNTS_OWNED_SELF("misc.accounts_owned_self", "%count"),
|
||||
|
||||
/** The player %name has %count accounts: */
|
||||
ACCOUNTS_OWNED_OTHER("accounts_owned_other", "%name", "%count"),
|
||||
ACCOUNTS_OWNED_OTHER("misc.accounts_owned_other", "%name", "%count"),
|
||||
|
||||
/** An admin just registered you; please log in again */
|
||||
KICK_FOR_ADMIN_REGISTER("kicked_admin_registered"),
|
||||
KICK_FOR_ADMIN_REGISTER("registration.kicked_admin_registered"),
|
||||
|
||||
/** Error: not all required settings are set for sending emails. Please contact an admin. */
|
||||
INCOMPLETE_EMAIL_SETTINGS("incomplete_email_settings"),
|
||||
INCOMPLETE_EMAIL_SETTINGS("email.incomplete_settings"),
|
||||
|
||||
/** The email could not be sent. Please contact an administrator. */
|
||||
EMAIL_SEND_FAILURE("email_send_failure"),
|
||||
EMAIL_SEND_FAILURE("email.send_failure"),
|
||||
|
||||
/** A recovery code to reset your password has been sent to your email. */
|
||||
RECOVERY_CODE_SENT("recovery_code_sent"),
|
||||
RECOVERY_CODE_SENT("recovery.code.code_sent"),
|
||||
|
||||
/** The recovery code is not correct! You have %count tries remaining. */
|
||||
INCORRECT_RECOVERY_CODE("recovery_code_incorrect", "%count"),
|
||||
INCORRECT_RECOVERY_CODE("recovery.code.incorrect", "%count"),
|
||||
|
||||
/**
|
||||
* You have exceeded the maximum number of attempts to enter the recovery code.
|
||||
* Use "/email recovery [email]" to generate a new one.
|
||||
*/
|
||||
RECOVERY_TRIES_EXCEEDED("recovery_tries_exceeded"),
|
||||
RECOVERY_TRIES_EXCEEDED("recovery.code.tries_exceeded"),
|
||||
|
||||
/** Recovery code entered correctly! */
|
||||
RECOVERY_CODE_CORRECT("recovery_code_correct"),
|
||||
RECOVERY_CODE_CORRECT("recovery.code.correct"),
|
||||
|
||||
/** Please use the command /email setpassword to change your password immediately. */
|
||||
RECOVERY_CHANGE_PASSWORD("recovery_change_password"),
|
||||
RECOVERY_CHANGE_PASSWORD("recovery.code.change_password"),
|
||||
|
||||
/** You cannot change your password using this command anymore. */
|
||||
CHANGE_PASSWORD_EXPIRED("change_password_expired"),
|
||||
CHANGE_PASSWORD_EXPIRED("email.change_password_expired"),
|
||||
|
||||
/** An email was already sent recently. You must wait %time before you can send a new one. */
|
||||
EMAIL_COOLDOWN_ERROR("email_cooldown_error", "%time"),
|
||||
EMAIL_COOLDOWN_ERROR("email.email_cooldown_error", "%time"),
|
||||
|
||||
/**
|
||||
* The command you are trying to execute is sensitive and requires a verification!
|
||||
* A verification code has been sent to your email,
|
||||
* run the command "/verification [code]" to verify your identity.
|
||||
* This command is sensitive and requires an email verification!
|
||||
* Check your inbox and follow the email's instructions.
|
||||
*/
|
||||
VERIFICATION_CODE_REQUIRED("verification_code_required"),
|
||||
VERIFICATION_CODE_REQUIRED("verification.code_required"),
|
||||
|
||||
/** Usage: /verification <code> */
|
||||
USAGE_VERIFICATION_CODE("usage_verification_code"),
|
||||
USAGE_VERIFICATION_CODE("verification.command_usage"),
|
||||
|
||||
/** Incorrect code, please type "/verification <code>" into the chat! */
|
||||
INCORRECT_VERIFICATION_CODE("incorrect_verification_code"),
|
||||
/** Incorrect code, please type "/verification <code>" into the chat, using the code you received by email */
|
||||
INCORRECT_VERIFICATION_CODE("verification.incorrect_code"),
|
||||
|
||||
/**
|
||||
* Your identity has been verified!
|
||||
* You can now execute every sensitive command within the current session!
|
||||
*/
|
||||
VERIFICATION_CODE_VERIFIED("verification_code_verified"),
|
||||
/** Your identity has been verified! You can now execute all commands within the current session! */
|
||||
VERIFICATION_CODE_VERIFIED("verification.success"),
|
||||
|
||||
/**
|
||||
* You can already execute every sensitive command within the current session!
|
||||
*/
|
||||
VERIFICATION_CODE_ALREADY_VERIFIED("verification_code_already_verified"),
|
||||
/** You can already execute every sensitive command within the current session! */
|
||||
VERIFICATION_CODE_ALREADY_VERIFIED("verification.already_verified"),
|
||||
|
||||
/** Your code has expired! Execute another sensitive command to get a new code! */
|
||||
VERIFICATION_CODE_EXPIRED("verification_code_expired"),
|
||||
VERIFICATION_CODE_EXPIRED("verification.code_expired"),
|
||||
|
||||
/** To verify your identity you need to link an email address with your account! */
|
||||
VERIFICATION_CODE_EMAIL_NEEDED("verification_code_email_needed"),
|
||||
VERIFICATION_CODE_EMAIL_NEEDED("verification.email_needed"),
|
||||
|
||||
/** You used a command too fast! Please, join the server again and wait more before using any command. */
|
||||
QUICK_COMMAND_PROTECTION_KICK("on_join_validation.quick_command"),
|
||||
|
||||
/** second */
|
||||
SECOND("second"),
|
||||
SECOND("time.second"),
|
||||
|
||||
/** seconds */
|
||||
SECONDS("seconds"),
|
||||
SECONDS("time.seconds"),
|
||||
|
||||
/** minute */
|
||||
MINUTE("minute"),
|
||||
MINUTE("time.minute"),
|
||||
|
||||
/** minutes */
|
||||
MINUTES("minutes"),
|
||||
MINUTES("time.minutes"),
|
||||
|
||||
/** hour */
|
||||
HOUR("hour"),
|
||||
HOUR("time.hour"),
|
||||
|
||||
/** hours */
|
||||
HOURS("hours"),
|
||||
HOURS("time.hours"),
|
||||
|
||||
/** day */
|
||||
DAY("day"),
|
||||
DAY("time.day"),
|
||||
|
||||
/** days */
|
||||
DAYS("days");
|
||||
DAYS("time.days");
|
||||
|
||||
|
||||
private String key;
|
||||
|
@ -2,10 +2,10 @@ package fr.xephi.authme.message;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.initialization.Reloadable;
|
||||
import fr.xephi.authme.util.expiring.Duration;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Map;
|
||||
@ -14,11 +14,15 @@ import java.util.concurrent.TimeUnit;
|
||||
/**
|
||||
* Class for retrieving and sending translatable messages to players.
|
||||
*/
|
||||
public class Messages implements Reloadable {
|
||||
public class Messages {
|
||||
|
||||
// Custom Authme tag replaced to new line
|
||||
private static final String NEWLINE_TAG = "%nl%";
|
||||
|
||||
// Global tag replacements
|
||||
private static final String USERNAME_TAG = "%username%";
|
||||
private static final String DISPLAYNAME_TAG = "%displayname%";
|
||||
|
||||
/** Contains the keys of the singular messages for time units. */
|
||||
private static final Map<TimeUnit, MessageKey> TIME_UNIT_SINGULARS = ImmutableMap.<TimeUnit, MessageKey>builder()
|
||||
.put(TimeUnit.SECONDS, MessageKey.SECOND)
|
||||
@ -33,16 +37,14 @@ public class Messages implements Reloadable {
|
||||
.put(TimeUnit.HOURS, MessageKey.HOURS)
|
||||
.put(TimeUnit.DAYS, MessageKey.DAYS).build();
|
||||
|
||||
private final MessageFileHandlerProvider messageFileHandlerProvider;
|
||||
private MessageFileHandler messageFileHandler;
|
||||
private MessagesFileHandler messagesFileHandler;
|
||||
|
||||
/*
|
||||
* Constructor.
|
||||
*/
|
||||
@Inject
|
||||
Messages(MessageFileHandlerProvider messageFileHandlerProvider) {
|
||||
this.messageFileHandlerProvider = messageFileHandlerProvider;
|
||||
reload();
|
||||
Messages(MessagesFileHandler messagesFileHandler) {
|
||||
this.messagesFileHandler = messagesFileHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,7 +54,7 @@ public class Messages implements Reloadable {
|
||||
* @param key The key of the message to send
|
||||
*/
|
||||
public void send(CommandSender sender, MessageKey key) {
|
||||
String[] lines = retrieve(key);
|
||||
String[] lines = retrieve(key, sender);
|
||||
for (String line : lines) {
|
||||
sender.sendMessage(line);
|
||||
}
|
||||
@ -68,7 +70,7 @@ public class Messages implements Reloadable {
|
||||
* @param replacements The replacements to apply for the tags
|
||||
*/
|
||||
public void send(CommandSender sender, MessageKey key, String... replacements) {
|
||||
String message = retrieveSingle(key, replacements);
|
||||
String message = retrieveSingle(sender, key, replacements);
|
||||
for (String line : message.split("\n")) {
|
||||
sender.sendMessage(line);
|
||||
}
|
||||
@ -78,10 +80,11 @@ public class Messages implements Reloadable {
|
||||
* Retrieve the message from the text file and return it split by new line as an array.
|
||||
*
|
||||
* @param key The message key to retrieve
|
||||
* @param sender The entity to send the message to
|
||||
* @return The message split by new lines
|
||||
*/
|
||||
public String[] retrieve(MessageKey key) {
|
||||
String message = retrieveMessage(key);
|
||||
public String[] retrieve(MessageKey key, CommandSender sender) {
|
||||
String message = retrieveMessage(key, sender);
|
||||
if (message.isEmpty()) {
|
||||
// Return empty array instead of array with 1 empty string as entry
|
||||
return new String[0];
|
||||
@ -102,18 +105,43 @@ public class Messages implements Reloadable {
|
||||
? TIME_UNIT_SINGULARS.get(duration.getTimeUnit())
|
||||
: TIME_UNIT_PLURALS.get(duration.getTimeUnit());
|
||||
|
||||
return value + " " + retrieveMessage(timeUnitKey);
|
||||
return value + " " + retrieveMessage(timeUnitKey, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the message from the text file.
|
||||
*
|
||||
* @param key The message key to retrieve
|
||||
* @param sender The entity to send the message to
|
||||
* @return The message from the file
|
||||
*/
|
||||
private String retrieveMessage(MessageKey key) {
|
||||
return formatMessage(
|
||||
messageFileHandler.getMessage(key.getKey()));
|
||||
private String retrieveMessage(MessageKey key, CommandSender sender) {
|
||||
String message = messagesFileHandler.getMessage(key.getKey());
|
||||
String displayName = sender.getName();
|
||||
if (sender instanceof Player) {
|
||||
displayName = ((Player) sender).getDisplayName();
|
||||
}
|
||||
|
||||
return ChatColor.translateAlternateColorCodes('&', message)
|
||||
.replace(NEWLINE_TAG, "\n")
|
||||
.replace(USERNAME_TAG, sender.getName())
|
||||
.replace(DISPLAYNAME_TAG, displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the message from the text file.
|
||||
*
|
||||
* @param key The message key to retrieve
|
||||
* @param name The name of the entity to send the message to
|
||||
* @return The message from the file
|
||||
*/
|
||||
private String retrieveMessage(MessageKey key, String name) {
|
||||
String message = messagesFileHandler.getMessage(key.getKey());
|
||||
|
||||
return ChatColor.translateAlternateColorCodes('&', message)
|
||||
.replace(NEWLINE_TAG, "\n")
|
||||
.replace(USERNAME_TAG, name)
|
||||
.replace(DISPLAYNAME_TAG, name);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,12 +149,13 @@ public class Messages implements Reloadable {
|
||||
* logs an error if the number of supplied replacements doesn't correspond to the number of tags
|
||||
* the message key contains.
|
||||
*
|
||||
* @param sender The entity to send the message to
|
||||
* @param key The key of the message to send
|
||||
* @param replacements The replacements to apply for the tags
|
||||
* @return The message from the file with replacements
|
||||
*/
|
||||
public String retrieveSingle(MessageKey key, String... replacements) {
|
||||
String message = retrieveMessage(key);
|
||||
public String retrieveSingle(CommandSender sender, MessageKey key, String... replacements) {
|
||||
String message = retrieveMessage(key, sender);
|
||||
String[] tags = key.getTags();
|
||||
if (replacements.length == tags.length) {
|
||||
for (int i = 0; i < tags.length; ++i) {
|
||||
@ -138,15 +167,26 @@ public class Messages implements Reloadable {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
this.messageFileHandler = messageFileHandlerProvider
|
||||
.initializeHandler(lang -> "messages/messages_" + lang + ".yml", "/authme messages");
|
||||
/**
|
||||
* Retrieve the given message code with the given tag replacements. Note that this method
|
||||
* logs an error if the number of supplied replacements doesn't correspond to the number of tags
|
||||
* the message key contains.
|
||||
*
|
||||
* @param name The name of the entity to send the message to
|
||||
* @param key The key of the message to send
|
||||
* @param replacements The replacements to apply for the tags
|
||||
* @return The message from the file with replacements
|
||||
*/
|
||||
public String retrieveSingle(String name, MessageKey key, String... replacements) {
|
||||
String message = retrieveMessage(key, name);
|
||||
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.warning("Invalid number of replacements for message key '" + key + "'");
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private static String formatMessage(String message) {
|
||||
return ChatColor.translateAlternateColorCodes('&', message)
|
||||
.replace(NEWLINE_TAG, "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
package fr.xephi.authme.message;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.message.updater.MessageUpdater;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* File handler for the messages_xx.yml resource.
|
||||
*/
|
||||
public class MessagesFileHandler extends AbstractMessageFileHandler {
|
||||
|
||||
@Inject
|
||||
private MessageUpdater messageUpdater;
|
||||
|
||||
MessagesFileHandler() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
reloadInternal(false);
|
||||
}
|
||||
|
||||
private void reloadInternal(boolean isFromReload) {
|
||||
super.reload();
|
||||
|
||||
String language = getLanguage();
|
||||
boolean hasChange = messageUpdater.migrateAndSave(
|
||||
getUserLanguageFile(), createFilePath(language), createFilePath(DEFAULT_LANGUAGE));
|
||||
if (hasChange) {
|
||||
if (isFromReload) {
|
||||
ConsoleLogger.warning("Migration after reload attempt");
|
||||
} else {
|
||||
reloadInternal(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String createFilePath(String language) {
|
||||
return "messages/messages_" + language + ".yml";
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package fr.xephi.authme.message.updater;
|
||||
|
||||
import ch.jalu.configme.properties.Property;
|
||||
import ch.jalu.configme.resource.PropertyReader;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.util.FileUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Returns messages from the JAR's message files. Favors a local JAR (e.g. messages_nl.yml)
|
||||
* before falling back to the default language (messages_en.yml).
|
||||
*/
|
||||
public class JarMessageSource {
|
||||
|
||||
private final PropertyReader localJarMessages;
|
||||
private final PropertyReader defaultJarMessages;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param localJarPath path to the messages file of the language the plugin is configured to use (may not exist)
|
||||
* @param defaultJarPath path to the default messages file in the JAR (must exist)
|
||||
*/
|
||||
public JarMessageSource(String localJarPath, String defaultJarPath) {
|
||||
localJarMessages = localJarPath.equals(defaultJarPath) ? null : loadJarFile(localJarPath);
|
||||
defaultJarMessages = loadJarFile(defaultJarPath);
|
||||
|
||||
if (defaultJarMessages == null) {
|
||||
throw new IllegalStateException("Default JAR file '" + defaultJarPath + "' could not be loaded");
|
||||
}
|
||||
}
|
||||
|
||||
public String getMessageFromJar(Property<?> property) {
|
||||
String key = property.getPath();
|
||||
String message = getString(key, localJarMessages);
|
||||
return message == null ? getString(key, defaultJarMessages) : message;
|
||||
}
|
||||
|
||||
private static String getString(String path, PropertyReader reader) {
|
||||
return reader == null ? null : reader.getTypedObject(path, String.class);
|
||||
}
|
||||
|
||||
private static MessageMigraterPropertyReader loadJarFile(String jarPath) {
|
||||
try (InputStream stream = FileUtils.getResourceFromJar(jarPath)) {
|
||||
if (stream == null) {
|
||||
ConsoleLogger.debug("Could not load '" + jarPath + "' from JAR");
|
||||
return null;
|
||||
}
|
||||
return MessageMigraterPropertyReader.loadFromStream(stream);
|
||||
} catch (IOException e) {
|
||||
ConsoleLogger.logException("Exception while handling JAR path '" + jarPath + "'", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
package fr.xephi.authme.message.updater;
|
||||
|
||||
import ch.jalu.configme.exception.ConfigMeException;
|
||||
import ch.jalu.configme.resource.PropertyReader;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Implementation of {@link PropertyReader} which can read a file or a stream with
|
||||
* a specified charset.
|
||||
*/
|
||||
public final class MessageMigraterPropertyReader implements PropertyReader {
|
||||
|
||||
public static final Charset CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
private Map<String, Object> root;
|
||||
/** See same field in {@link ch.jalu.configme.resource.YamlFileReader} for details. */
|
||||
private boolean hasObjectAsRoot = false;
|
||||
|
||||
private MessageMigraterPropertyReader(Map<String, Object> valuesMap) {
|
||||
root = valuesMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new property reader for the given file.
|
||||
*
|
||||
* @param file the file to load
|
||||
* @return the created property reader
|
||||
*/
|
||||
public static MessageMigraterPropertyReader loadFromFile(File file) {
|
||||
Map<String, Object> valuesMap;
|
||||
try (InputStream is = new FileInputStream(file)) {
|
||||
valuesMap = readStreamToMap(is);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Error while reading file '" + file + "'", e);
|
||||
}
|
||||
|
||||
return new MessageMigraterPropertyReader(valuesMap);
|
||||
}
|
||||
|
||||
public static MessageMigraterPropertyReader loadFromStream(InputStream inputStream) {
|
||||
Map<String, Object> valuesMap = readStreamToMap(inputStream);
|
||||
return new MessageMigraterPropertyReader(valuesMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(String path) {
|
||||
if (path.isEmpty()) {
|
||||
return hasObjectAsRoot ? root.get("") : root;
|
||||
}
|
||||
Object node = root;
|
||||
String[] keys = path.split("\\.");
|
||||
for (String key : keys) {
|
||||
node = getIfIsMap(key, node);
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getTypedObject(String path, Class<T> clazz) {
|
||||
Object value = getObject(path);
|
||||
if (clazz.isInstance(value)) {
|
||||
return clazz.cast(value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String path, Object value) {
|
||||
Objects.requireNonNull(path);
|
||||
|
||||
if (path.isEmpty()) {
|
||||
root.clear();
|
||||
root.put("", value);
|
||||
hasObjectAsRoot = true;
|
||||
} else if (hasObjectAsRoot) {
|
||||
throw new ConfigMeException("The root path is a bean property; you cannot set values to any subpath. "
|
||||
+ "Modify the bean at the root or set a new one instead.");
|
||||
} else {
|
||||
setValueInChildPath(path, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value at the given path. This method is used when the root is a map and not a specific object.
|
||||
*
|
||||
* @param path the path to set the value at
|
||||
* @param value the value to set
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void setValueInChildPath(String path, Object value) {
|
||||
Map<String, Object> node = root;
|
||||
String[] keys = path.split("\\.");
|
||||
for (int i = 0; i < keys.length - 1; ++i) {
|
||||
Object child = node.get(keys[i]);
|
||||
if (child instanceof Map<?, ?>) {
|
||||
node = (Map<String, Object>) child;
|
||||
} else { // child is null or some other value - replace with map
|
||||
Map<String, Object> newEntry = new HashMap<>();
|
||||
node.put(keys[i], newEntry);
|
||||
if (value == null) {
|
||||
// For consistency, replace whatever value/null here with an empty map,
|
||||
// but if the value is null our work here is done.
|
||||
return;
|
||||
}
|
||||
node = newEntry;
|
||||
}
|
||||
}
|
||||
// node now contains the parent map (existing or newly created)
|
||||
if (value == null) {
|
||||
node.remove(keys[keys.length - 1]);
|
||||
} else {
|
||||
node.put(keys[keys.length - 1], value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
throw new UnsupportedOperationException("Reload not supported by this implementation");
|
||||
}
|
||||
|
||||
private static Map<String, Object> readStreamToMap(InputStream inputStream) {
|
||||
try (InputStreamReader isr = new InputStreamReader(inputStream, CHARSET)) {
|
||||
Object obj = new Yaml().load(isr);
|
||||
return obj == null ? new HashMap<>() : (Map<String, Object>) obj;
|
||||
} catch (IOException e) {
|
||||
throw new ConfigMeException("Could not read stream", e);
|
||||
} catch (ClassCastException e) {
|
||||
throw new ConfigMeException("Top-level is not a map", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getIfIsMap(String key, Object value) {
|
||||
if (value instanceof Map<?, ?>) {
|
||||
return ((Map<?, ?>) value).get(key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
package fr.xephi.authme.message.updater;
|
||||
|
||||
import ch.jalu.configme.SettingsManager;
|
||||
import ch.jalu.configme.configurationdata.ConfigurationData;
|
||||
import ch.jalu.configme.configurationdata.PropertyListBuilder;
|
||||
import ch.jalu.configme.properties.Property;
|
||||
import ch.jalu.configme.properties.StringProperty;
|
||||
import ch.jalu.configme.resource.PropertyResource;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.io.Files;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.util.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Migrates the used messages file to a complete, up-to-date version when necessary.
|
||||
*/
|
||||
public class MessageUpdater {
|
||||
|
||||
/**
|
||||
* Configuration data object for all message keys incl. comments associated to sections.
|
||||
*/
|
||||
private static final ConfigurationData CONFIGURATION_DATA = buildConfigurationData();
|
||||
|
||||
public static ConfigurationData getConfigurationData() {
|
||||
return CONFIGURATION_DATA;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies any necessary migrations to the user's messages file and saves it if it has been modified.
|
||||
*
|
||||
* @param userFile the user's messages file (yml file in the plugin's folder)
|
||||
* @param localJarPath path to the messages file in the JAR for the same language (may not exist)
|
||||
* @param defaultJarPath path to the messages file in the JAR for the default language
|
||||
* @return true if the file has been migrated and saved, false if it is up-to-date
|
||||
*/
|
||||
public boolean migrateAndSave(File userFile, String localJarPath, String defaultJarPath) {
|
||||
JarMessageSource jarMessageSource = new JarMessageSource(localJarPath, defaultJarPath);
|
||||
return migrateAndSave(userFile, jarMessageSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the migration.
|
||||
*
|
||||
* @param userFile the file to verify and migrate
|
||||
* @param jarMessageSource jar message source to get texts from if missing
|
||||
* @return true if the file has been migrated and saved, false if it is up-to-date
|
||||
*/
|
||||
private boolean migrateAndSave(File userFile, JarMessageSource jarMessageSource) {
|
||||
// YamlConfiguration escapes all special characters when saving, making the file hard to use, so use ConfigMe
|
||||
PropertyResource userResource = new MigraterYamlFileResource(userFile);
|
||||
|
||||
// Step 1: Migrate any old keys in the file to the new paths
|
||||
boolean movedOldKeys = migrateOldKeys(userResource);
|
||||
// Step 2: Perform newer migrations
|
||||
boolean movedNewerKeys = migrateKeys(userResource);
|
||||
// Step 3: Take any missing messages from the message files shipped in the AuthMe JAR
|
||||
boolean addedMissingKeys = addMissingKeys(jarMessageSource, userResource);
|
||||
|
||||
if (movedOldKeys || movedNewerKeys || addedMissingKeys) {
|
||||
backupMessagesFile(userFile);
|
||||
|
||||
SettingsManager settingsManager = new SettingsManager(userResource, null, CONFIGURATION_DATA);
|
||||
settingsManager.save();
|
||||
ConsoleLogger.debug("Successfully saved {0}", userFile);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean migrateKeys(PropertyResource userResource) {
|
||||
return moveIfApplicable(userResource, "misc.two_factor_create", MessageKey.TWO_FACTOR_CREATE.getKey());
|
||||
}
|
||||
|
||||
private static boolean moveIfApplicable(PropertyResource resource, String oldPath, String newPath) {
|
||||
if (resource.getString(newPath) == null && resource.getString(oldPath) != null) {
|
||||
resource.setValue(newPath, resource.getString(oldPath));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean migrateOldKeys(PropertyResource userResource) {
|
||||
boolean hasChange = OldMessageKeysMigrater.migrateOldPaths(userResource);
|
||||
if (hasChange) {
|
||||
ConsoleLogger.info("Old keys have been moved to the new ones in your messages_xx.yml file");
|
||||
}
|
||||
return hasChange;
|
||||
}
|
||||
|
||||
private boolean addMissingKeys(JarMessageSource jarMessageSource, PropertyResource userResource) {
|
||||
List<String> addedKeys = new ArrayList<>();
|
||||
for (Property<?> property : CONFIGURATION_DATA.getProperties()) {
|
||||
final String key = property.getPath();
|
||||
if (userResource.getString(key) == null) {
|
||||
userResource.setValue(key, jarMessageSource.getMessageFromJar(property));
|
||||
addedKeys.add(key);
|
||||
}
|
||||
}
|
||||
if (!addedKeys.isEmpty()) {
|
||||
ConsoleLogger.info(
|
||||
"Added " + addedKeys.size() + " missing keys to your messages_xx.yml file: " + addedKeys);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void backupMessagesFile(File messagesFile) {
|
||||
String backupName = FileUtils.createBackupFilePath(messagesFile);
|
||||
File backupFile = new File(backupName);
|
||||
try {
|
||||
Files.copy(messagesFile, backupFile);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Could not back up '" + messagesFile + "' to '" + backupFile + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the {@link ConfigurationData} for exporting a messages file in its entirety.
|
||||
*
|
||||
* @return the configuration data to export with
|
||||
*/
|
||||
private static ConfigurationData buildConfigurationData() {
|
||||
Map<String, String[]> comments = ImmutableMap.<String, String[]>builder()
|
||||
.put("registration", new String[]{"Registration"})
|
||||
.put("password", new String[]{"Password errors on registration"})
|
||||
.put("login", new String[]{"Login"})
|
||||
.put("error", new String[]{"Errors"})
|
||||
.put("antibot", new String[]{"AntiBot"})
|
||||
.put("unregister", new String[]{"Unregister"})
|
||||
.put("misc", new String[]{"Other messages"})
|
||||
.put("session", new String[]{"Session messages"})
|
||||
.put("on_join_validation", new String[]{"Error messages when joining"})
|
||||
.put("email", new String[]{"Email"})
|
||||
.put("recovery", new String[]{"Password recovery by email"})
|
||||
.put("captcha", new String[]{"Captcha"})
|
||||
.put("verification", new String[]{"Verification code"})
|
||||
.put("time", new String[]{"Time units"})
|
||||
.put("two_factor", new String[]{"Two-factor authentication"})
|
||||
.build();
|
||||
|
||||
Set<String> addedKeys = new HashSet<>();
|
||||
PropertyListBuilder builder = new PropertyListBuilder();
|
||||
// Add one key per section based on the comments map above so that the order is clear
|
||||
for (String path : comments.keySet()) {
|
||||
MessageKey key = Arrays.stream(MessageKey.values()).filter(p -> p.getKey().startsWith(path + "."))
|
||||
.findFirst().orElseThrow(() -> new IllegalStateException(path));
|
||||
builder.add(new StringProperty(key.getKey(), ""));
|
||||
addedKeys.add(key.getKey());
|
||||
}
|
||||
// Add all remaining keys to the property list builder
|
||||
Arrays.stream(MessageKey.values())
|
||||
.filter(key -> !addedKeys.contains(key.getKey()))
|
||||
.forEach(key -> builder.add(new StringProperty(key.getKey(), "")));
|
||||
|
||||
return new ConfigurationData(builder.create(), comments);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package fr.xephi.authme.message.updater;
|
||||
|
||||
import ch.jalu.configme.beanmapper.leafproperties.LeafPropertiesGenerator;
|
||||
import ch.jalu.configme.configurationdata.ConfigurationData;
|
||||
import ch.jalu.configme.exception.ConfigMeException;
|
||||
import ch.jalu.configme.properties.Property;
|
||||
import ch.jalu.configme.resource.PropertyPathTraverser;
|
||||
import ch.jalu.configme.resource.YamlFileResource;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.List;
|
||||
|
||||
import static fr.xephi.authme.message.updater.MessageMigraterPropertyReader.CHARSET;
|
||||
|
||||
/**
|
||||
* Extension of {@link YamlFileResource} to fine-tune the export style
|
||||
* and to be able to specify the character encoding.
|
||||
*/
|
||||
public class MigraterYamlFileResource extends YamlFileResource {
|
||||
|
||||
private static final String INDENTATION = " ";
|
||||
|
||||
private final File file;
|
||||
private Yaml singleQuoteYaml;
|
||||
|
||||
public MigraterYamlFileResource(File file) {
|
||||
super(file, MessageMigraterPropertyReader.loadFromFile(file), new LeafPropertiesGenerator());
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Yaml getSingleQuoteYaml() {
|
||||
if (singleQuoteYaml == null) {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
options.setAllowUnicode(true);
|
||||
options.setDefaultScalarStyle(DumperOptions.ScalarStyle.SINGLE_QUOTED);
|
||||
// Overridden setting: don't split lines
|
||||
options.setSplitLines(false);
|
||||
singleQuoteYaml = new Yaml(options);
|
||||
}
|
||||
return singleQuoteYaml;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportProperties(ConfigurationData configurationData) {
|
||||
try (FileOutputStream fos = new FileOutputStream(file);
|
||||
OutputStreamWriter writer = new OutputStreamWriter(fos, CHARSET)) {
|
||||
PropertyPathTraverser pathTraverser = new PropertyPathTraverser(configurationData);
|
||||
for (Property<?> property : convertPropertiesToExportableTypes(configurationData.getProperties())) {
|
||||
|
||||
List<PropertyPathTraverser.PathElement> pathElements = pathTraverser.getPathElements(property);
|
||||
for (PropertyPathTraverser.PathElement pathElement : pathElements) {
|
||||
writeComments(writer, pathElement.indentationLevel, pathElement.comments);
|
||||
writer.append("\n")
|
||||
.append(indent(pathElement.indentationLevel))
|
||||
.append(pathElement.name)
|
||||
.append(":");
|
||||
}
|
||||
|
||||
writer.append(" ")
|
||||
.append(toYaml(property, pathElements.get(pathElements.size() - 1).indentationLevel));
|
||||
}
|
||||
writer.flush();
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
throw new ConfigMeException("Could not save config to '" + file.getPath() + "'", e);
|
||||
} finally {
|
||||
singleQuoteYaml = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeComments(Writer writer, int indentation, String[] comments) throws IOException {
|
||||
if (comments.length == 0) {
|
||||
return;
|
||||
}
|
||||
String commentStart = "\n" + indent(indentation) + "# ";
|
||||
for (String comment : comments) {
|
||||
writer.append(commentStart).append(comment);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> String toYaml(Property<T> property, int indent) {
|
||||
Object value = property.getValue(this);
|
||||
String representation = transformValue(property, value);
|
||||
String[] lines = representation.split("\\n");
|
||||
return String.join("\n" + indent(indent), lines);
|
||||
}
|
||||
|
||||
private static String indent(int level) {
|
||||
String result = "";
|
||||
for (int i = 0; i < level; i++) {
|
||||
result += INDENTATION;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
package fr.xephi.authme.message.updater;
|
||||
|
||||
import ch.jalu.configme.resource.PropertyResource;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.google.common.collect.ImmutableMap.of;
|
||||
|
||||
/**
|
||||
* Migrates message files from the old keys (before 5.5) to the new ones.
|
||||
*
|
||||
* @see <a href="https://github.com/AuthMe/AuthMeReloaded/issues/1467">Issue #1467</a>
|
||||
*/
|
||||
final class OldMessageKeysMigrater {
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
static final Map<MessageKey, String> KEYS_TO_OLD_PATH = ImmutableMap.<MessageKey, String>builder()
|
||||
.put(MessageKey.LOGIN_SUCCESS, "login")
|
||||
.put(MessageKey.ERROR, "error")
|
||||
.put(MessageKey.DENIED_COMMAND, "denied_command")
|
||||
.put(MessageKey.SAME_IP_ONLINE, "same_ip_online")
|
||||
.put(MessageKey.DENIED_CHAT, "denied_chat")
|
||||
.put(MessageKey.KICK_ANTIBOT, "kick_antibot")
|
||||
.put(MessageKey.UNKNOWN_USER, "unknown_user")
|
||||
.put(MessageKey.NOT_LOGGED_IN, "not_logged_in")
|
||||
.put(MessageKey.USAGE_LOGIN, "usage_log")
|
||||
.put(MessageKey.WRONG_PASSWORD, "wrong_pwd")
|
||||
.put(MessageKey.UNREGISTERED_SUCCESS, "unregistered")
|
||||
.put(MessageKey.REGISTRATION_DISABLED, "reg_disabled")
|
||||
.put(MessageKey.SESSION_RECONNECTION, "valid_session")
|
||||
.put(MessageKey.ACCOUNT_NOT_ACTIVATED, "vb_nonActiv")
|
||||
.put(MessageKey.NAME_ALREADY_REGISTERED, "user_regged")
|
||||
.put(MessageKey.NO_PERMISSION, "no_perm")
|
||||
.put(MessageKey.LOGIN_MESSAGE, "login_msg")
|
||||
.put(MessageKey.REGISTER_MESSAGE, "reg_msg")
|
||||
.put(MessageKey.MAX_REGISTER_EXCEEDED, "max_reg")
|
||||
.put(MessageKey.USAGE_REGISTER, "usage_reg")
|
||||
.put(MessageKey.USAGE_UNREGISTER, "usage_unreg")
|
||||
.put(MessageKey.PASSWORD_CHANGED_SUCCESS, "pwd_changed")
|
||||
.put(MessageKey.PASSWORD_MATCH_ERROR, "password_error")
|
||||
.put(MessageKey.PASSWORD_IS_USERNAME_ERROR, "password_error_nick")
|
||||
.put(MessageKey.PASSWORD_UNSAFE_ERROR, "password_error_unsafe")
|
||||
.put(MessageKey.PASSWORD_CHARACTERS_ERROR, "password_error_chars")
|
||||
.put(MessageKey.SESSION_EXPIRED, "invalid_session")
|
||||
.put(MessageKey.MUST_REGISTER_MESSAGE, "reg_only")
|
||||
.put(MessageKey.ALREADY_LOGGED_IN_ERROR, "logged_in")
|
||||
.put(MessageKey.LOGOUT_SUCCESS, "logout")
|
||||
.put(MessageKey.USERNAME_ALREADY_ONLINE_ERROR, "same_nick")
|
||||
.put(MessageKey.REGISTER_SUCCESS, "registered")
|
||||
.put(MessageKey.INVALID_PASSWORD_LENGTH, "pass_len")
|
||||
.put(MessageKey.CONFIG_RELOAD_SUCCESS, "reload")
|
||||
.put(MessageKey.LOGIN_TIMEOUT_ERROR, "timeout")
|
||||
.put(MessageKey.USAGE_CHANGE_PASSWORD, "usage_changepassword")
|
||||
.put(MessageKey.INVALID_NAME_LENGTH, "name_len")
|
||||
.put(MessageKey.INVALID_NAME_CHARACTERS, "regex")
|
||||
.put(MessageKey.ADD_EMAIL_MESSAGE, "add_email")
|
||||
.put(MessageKey.FORGOT_PASSWORD_MESSAGE, "recovery_email")
|
||||
.put(MessageKey.USAGE_CAPTCHA, "usage_captcha")
|
||||
.put(MessageKey.CAPTCHA_WRONG_ERROR, "wrong_captcha")
|
||||
.put(MessageKey.CAPTCHA_SUCCESS, "valid_captcha")
|
||||
.put(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, "captcha_for_registration")
|
||||
.put(MessageKey.REGISTER_CAPTCHA_SUCCESS, "register_captcha_valid")
|
||||
.put(MessageKey.KICK_FOR_VIP, "kick_forvip")
|
||||
.put(MessageKey.KICK_FULL_SERVER, "kick_fullserver")
|
||||
.put(MessageKey.USAGE_ADD_EMAIL, "usage_email_add")
|
||||
.put(MessageKey.USAGE_CHANGE_EMAIL, "usage_email_change")
|
||||
.put(MessageKey.USAGE_RECOVER_EMAIL, "usage_email_recovery")
|
||||
.put(MessageKey.INVALID_NEW_EMAIL, "new_email_invalid")
|
||||
.put(MessageKey.INVALID_OLD_EMAIL, "old_email_invalid")
|
||||
.put(MessageKey.INVALID_EMAIL, "email_invalid")
|
||||
.put(MessageKey.EMAIL_ADDED_SUCCESS, "email_added")
|
||||
.put(MessageKey.CONFIRM_EMAIL_MESSAGE, "email_confirm")
|
||||
.put(MessageKey.EMAIL_CHANGED_SUCCESS, "email_changed")
|
||||
.put(MessageKey.EMAIL_SHOW, "email_show")
|
||||
.put(MessageKey.SHOW_NO_EMAIL, "show_no_email")
|
||||
.put(MessageKey.RECOVERY_EMAIL_SENT_MESSAGE, "email_send")
|
||||
.put(MessageKey.COUNTRY_BANNED_ERROR, "country_banned")
|
||||
.put(MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE, "antibot_auto_enabled")
|
||||
.put(MessageKey.ANTIBOT_AUTO_DISABLED_MESSAGE, "antibot_auto_disabled")
|
||||
.put(MessageKey.EMAIL_ALREADY_USED_ERROR, "email_already_used")
|
||||
.put(MessageKey.TWO_FACTOR_CREATE, "two_factor_create")
|
||||
.put(MessageKey.NOT_OWNER_ERROR, "not_owner_error")
|
||||
.put(MessageKey.INVALID_NAME_CASE, "invalid_name_case")
|
||||
.put(MessageKey.TEMPBAN_MAX_LOGINS, "tempban_max_logins")
|
||||
.put(MessageKey.ACCOUNTS_OWNED_SELF, "accounts_owned_self")
|
||||
.put(MessageKey.ACCOUNTS_OWNED_OTHER, "accounts_owned_other")
|
||||
.put(MessageKey.KICK_FOR_ADMIN_REGISTER, "kicked_admin_registered")
|
||||
.put(MessageKey.INCOMPLETE_EMAIL_SETTINGS, "incomplete_email_settings")
|
||||
.put(MessageKey.EMAIL_SEND_FAILURE, "email_send_failure")
|
||||
.put(MessageKey.RECOVERY_CODE_SENT, "recovery_code_sent")
|
||||
.put(MessageKey.INCORRECT_RECOVERY_CODE, "recovery_code_incorrect")
|
||||
.put(MessageKey.RECOVERY_TRIES_EXCEEDED, "recovery_tries_exceeded")
|
||||
.put(MessageKey.RECOVERY_CODE_CORRECT, "recovery_code_correct")
|
||||
.put(MessageKey.RECOVERY_CHANGE_PASSWORD, "recovery_change_password")
|
||||
.put(MessageKey.CHANGE_PASSWORD_EXPIRED, "change_password_expired")
|
||||
.put(MessageKey.EMAIL_COOLDOWN_ERROR, "email_cooldown_error")
|
||||
.put(MessageKey.VERIFICATION_CODE_REQUIRED, "verification_code_required")
|
||||
.put(MessageKey.USAGE_VERIFICATION_CODE, "usage_verification_code")
|
||||
.put(MessageKey.INCORRECT_VERIFICATION_CODE, "incorrect_verification_code")
|
||||
.put(MessageKey.VERIFICATION_CODE_VERIFIED, "verification_code_verified")
|
||||
.put(MessageKey.VERIFICATION_CODE_ALREADY_VERIFIED, "verification_code_already_verified")
|
||||
.put(MessageKey.VERIFICATION_CODE_EXPIRED, "verification_code_expired")
|
||||
.put(MessageKey.VERIFICATION_CODE_EMAIL_NEEDED, "verification_code_email_needed")
|
||||
.put(MessageKey.SECOND, "second")
|
||||
.put(MessageKey.SECONDS, "seconds")
|
||||
.put(MessageKey.MINUTE, "minute")
|
||||
.put(MessageKey.MINUTES, "minutes")
|
||||
.put(MessageKey.HOUR, "hour")
|
||||
.put(MessageKey.HOURS, "hours")
|
||||
.put(MessageKey.DAY, "day")
|
||||
.put(MessageKey.DAYS, "days")
|
||||
.build();
|
||||
|
||||
private static final Map<MessageKey, Map<String, String>> PLACEHOLDER_REPLACEMENTS =
|
||||
ImmutableMap.<MessageKey, Map<String, String>>builder()
|
||||
.put(MessageKey.PASSWORD_CHARACTERS_ERROR, of("REG_EX", "%valid_chars"))
|
||||
.put(MessageKey.INVALID_NAME_CHARACTERS, of("REG_EX", "%valid_chars"))
|
||||
.put(MessageKey.USAGE_CAPTCHA, of("<theCaptcha>", "%captcha_code"))
|
||||
.put(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, of("<theCaptcha>", "%captcha_code"))
|
||||
.put(MessageKey.CAPTCHA_WRONG_ERROR, of("THE_CAPTCHA", "%captcha_code"))
|
||||
.build();
|
||||
|
||||
private OldMessageKeysMigrater() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates any existing old key paths to their new paths if no text has been defined for the new key.
|
||||
*
|
||||
* @param resource the resource to modify and read from
|
||||
* @return true if at least one message could be migrated, false otherwise
|
||||
*/
|
||||
static boolean migrateOldPaths(PropertyResource resource) {
|
||||
boolean wasPropertyMoved = false;
|
||||
for (Map.Entry<MessageKey, String> migrationEntry : KEYS_TO_OLD_PATH.entrySet()) {
|
||||
wasPropertyMoved |= moveIfApplicable(resource, migrationEntry.getKey(), migrationEntry.getValue());
|
||||
}
|
||||
return wasPropertyMoved;
|
||||
}
|
||||
|
||||
private static boolean moveIfApplicable(PropertyResource resource, MessageKey messageKey, String oldPath) {
|
||||
if (resource.getString(messageKey.getKey()) == null) {
|
||||
String textAtOldPath = resource.getString(oldPath);
|
||||
if (textAtOldPath != null) {
|
||||
textAtOldPath = replaceOldPlaceholders(messageKey, textAtOldPath);
|
||||
resource.setValue(messageKey.getKey(), textAtOldPath);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String replaceOldPlaceholders(MessageKey key, String text) {
|
||||
Map<String, String> replacements = PLACEHOLDER_REPLACEMENTS.get(key);
|
||||
if (replacements == null) {
|
||||
return text;
|
||||
}
|
||||
|
||||
String newText = text;
|
||||
for (Map.Entry<String, String> replacement : replacements.entrySet()) {
|
||||
newText = newText.replace(replacement.getKey(), replacement.getValue());
|
||||
}
|
||||
return newText;
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import fr.xephi.authme.permission.handlers.BPermissionsHandler;
|
||||
import fr.xephi.authme.permission.handlers.LuckPermsHandler;
|
||||
import fr.xephi.authme.permission.handlers.PermissionHandler;
|
||||
import fr.xephi.authme.permission.handlers.PermissionHandlerException;
|
||||
import fr.xephi.authme.permission.handlers.PermissionLoadUserException;
|
||||
import fr.xephi.authme.permission.handlers.PermissionsExHandler;
|
||||
import fr.xephi.authme.permission.handlers.VaultHandler;
|
||||
import fr.xephi.authme.permission.handlers.ZPermissionsHandler;
|
||||
@ -50,14 +51,8 @@ public class PermissionsManager implements Reloadable {
|
||||
*/
|
||||
private PermissionHandler handler = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param server Server instance
|
||||
* @param pluginManager Bukkit plugin manager
|
||||
*/
|
||||
@Inject
|
||||
public PermissionsManager(Server server, PluginManager pluginManager, Settings settings) {
|
||||
PermissionsManager(Server server, PluginManager pluginManager, Settings settings) {
|
||||
this.server = server;
|
||||
this.pluginManager = pluginManager;
|
||||
this.settings = settings;
|
||||
@ -116,7 +111,9 @@ public class PermissionsManager implements Reloadable {
|
||||
* Creates a permission handler for the provided permission systems if possible.
|
||||
*
|
||||
* @param type the permission systems type for which to create a corresponding permission handler
|
||||
*
|
||||
* @return the permission handler, or {@code null} if not possible
|
||||
*
|
||||
* @throws PermissionHandlerException during initialization of the permission handler
|
||||
*/
|
||||
private PermissionHandler createPermissionHandler(PermissionsSystemType type) throws PermissionHandlerException {
|
||||
@ -234,8 +231,9 @@ public class PermissionsManager implements Reloadable {
|
||||
/**
|
||||
* Check if the given player has permission for the given permission node.
|
||||
*
|
||||
* @param joiningPlayer The player to check
|
||||
* @param joiningPlayer The player to check
|
||||
* @param permissionNode The permission node to verify
|
||||
*
|
||||
* @return true if the player has permission, false otherwise
|
||||
*/
|
||||
public boolean hasPermission(JoiningPlayer joiningPlayer, PermissionNode permissionNode) {
|
||||
@ -264,6 +262,15 @@ public class PermissionsManager implements Reloadable {
|
||||
return handler.hasPermissionOffline(player.getName(), permissionNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the offline player with the given name has permission for the given permission node.
|
||||
* This method is used as a last resort when nothing besides the name is known.
|
||||
*
|
||||
* @param name The name of the player
|
||||
* @param permissionNode The permission node to verify
|
||||
*
|
||||
* @return true if the player has permission, false otherwise
|
||||
*/
|
||||
public boolean hasPermissionOffline(String name, PermissionNode permissionNode) {
|
||||
if (permissionNode == null) {
|
||||
return true;
|
||||
@ -314,7 +321,7 @@ public class PermissionsManager implements Reloadable {
|
||||
* @param groupName The group name.
|
||||
*
|
||||
* @return True if the player is in the specified group, false otherwise.
|
||||
* False is also returned if groups aren't supported by the used permissions system.
|
||||
* False is also returned if groups aren't supported by the used permissions system.
|
||||
*/
|
||||
public boolean isInGroup(OfflinePlayer player, String groupName) {
|
||||
return isEnabled() && handler.isInGroup(player, groupName);
|
||||
@ -327,7 +334,7 @@ public class PermissionsManager implements Reloadable {
|
||||
* @param groupName The name of the group.
|
||||
*
|
||||
* @return True if succeed, false otherwise.
|
||||
* False is also returned if this feature isn't supported for the current permissions system.
|
||||
* False is also returned if this feature isn't supported for the current permissions system.
|
||||
*/
|
||||
public boolean addGroup(OfflinePlayer player, String groupName) {
|
||||
if (!isEnabled() || StringUtils.isEmpty(groupName)) {
|
||||
@ -343,7 +350,7 @@ public class PermissionsManager implements Reloadable {
|
||||
* @param groupNames The name of the groups to add.
|
||||
*
|
||||
* @return True if at least one group was added, false otherwise.
|
||||
* False is also returned if this feature isn't supported for the current permissions system.
|
||||
* False is also returned if this feature isn't supported for the current permissions system.
|
||||
*/
|
||||
public boolean addGroups(OfflinePlayer player, Collection<String> groupNames) {
|
||||
// If no permissions system is used, return false
|
||||
@ -370,7 +377,7 @@ public class PermissionsManager implements Reloadable {
|
||||
* @param groupName The name of the group.
|
||||
*
|
||||
* @return True if succeed, false otherwise.
|
||||
* False is also returned if this feature isn't supported for the current permissions system.
|
||||
* False is also returned if this feature isn't supported for the current permissions system.
|
||||
*/
|
||||
public boolean removeGroup(OfflinePlayer player, String groupName) {
|
||||
return isEnabled() && handler.removeFromGroup(player, groupName);
|
||||
@ -383,7 +390,7 @@ public class PermissionsManager implements Reloadable {
|
||||
* @param groupNames The name of the groups to remove.
|
||||
*
|
||||
* @return True if at least one group was removed, false otherwise.
|
||||
* False is also returned if this feature isn't supported for the current permissions system.
|
||||
* False is also returned if this feature isn't supported for the current permissions system.
|
||||
*/
|
||||
public boolean removeGroups(OfflinePlayer player, Collection<String> groupNames) {
|
||||
// If no permissions system is used, return false
|
||||
@ -411,7 +418,7 @@ public class PermissionsManager implements Reloadable {
|
||||
* @param groupName The name of the group.
|
||||
*
|
||||
* @return True if succeed, false otherwise.
|
||||
* False is also returned if this feature isn't supported for the current permissions system.
|
||||
* False is also returned if this feature isn't supported for the current permissions system.
|
||||
*/
|
||||
public boolean setGroup(OfflinePlayer player, String groupName) {
|
||||
return isEnabled() && handler.setGroup(player, groupName);
|
||||
@ -425,7 +432,7 @@ public class PermissionsManager implements Reloadable {
|
||||
* @param player The player to remove all groups from.
|
||||
*
|
||||
* @return True if succeed, false otherwise.
|
||||
* False will also be returned if this feature isn't supported for the used permissions system.
|
||||
* False will also be returned if this feature isn't supported for the used permissions system.
|
||||
*/
|
||||
public boolean removeAllGroups(OfflinePlayer player) {
|
||||
// If no permissions system is used, return false
|
||||
@ -440,15 +447,47 @@ public class PermissionsManager implements Reloadable {
|
||||
return removeGroups(player, groupNames);
|
||||
}
|
||||
|
||||
public void loadUserData(UUID uuid) {
|
||||
if(!isEnabled()) {
|
||||
/**
|
||||
* Loads the permission data of the given player.
|
||||
*
|
||||
* @param offlinePlayer the offline player.
|
||||
* @return true if the load was successful.
|
||||
*/
|
||||
public boolean loadUserData(OfflinePlayer offlinePlayer) {
|
||||
try {
|
||||
try {
|
||||
loadUserData(offlinePlayer.getUniqueId());
|
||||
} catch (NoSuchMethodError e) {
|
||||
loadUserData(offlinePlayer.getName());
|
||||
}
|
||||
} catch (PermissionLoadUserException e) {
|
||||
ConsoleLogger.logException("Unable to load the permission data of user " + offlinePlayer.getName(), e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the permission data of the given player unique identifier.
|
||||
*
|
||||
* @param uuid the {@link UUID} of the player.
|
||||
* @throws PermissionLoadUserException if the action failed.
|
||||
*/
|
||||
public void loadUserData(UUID uuid) throws PermissionLoadUserException {
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
handler.loadUserData(uuid);
|
||||
}
|
||||
|
||||
public void loadUserData(String name) {
|
||||
if(!isEnabled()) {
|
||||
/**
|
||||
* Loads the permission data of the given player name.
|
||||
*
|
||||
* @param name the name of the player.
|
||||
* @throws PermissionLoadUserException if the action failed.
|
||||
*/
|
||||
public void loadUserData(String name) throws PermissionLoadUserException {
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
handler.loadUserData(name);
|
||||
|
@ -68,7 +68,22 @@ public enum PlayerPermission implements PermissionNode {
|
||||
/**
|
||||
* Permission to use the email verification codes feature.
|
||||
*/
|
||||
VERIFICATION_CODE("authme.player.security.verificationcode");
|
||||
VERIFICATION_CODE("authme.player.security.verificationcode"),
|
||||
|
||||
/**
|
||||
* Permission that enables on join quick commands checks for the player.
|
||||
*/
|
||||
QUICK_COMMANDS_PROTECTION("authme.player.protection.quickcommandsprotection"),
|
||||
|
||||
/**
|
||||
* Permission to enable two-factor authentication.
|
||||
*/
|
||||
ENABLE_TWO_FACTOR_AUTH("authme.player.totpadd"),
|
||||
|
||||
/**
|
||||
* Permission to disable two-factor authentication.
|
||||
*/
|
||||
DISABLE_TWO_FACTOR_AUTH("authme.player.totpremove");
|
||||
|
||||
/**
|
||||
* The permission node.
|
||||
|
@ -19,7 +19,7 @@ public enum PlayerStatePermission implements PermissionNode {
|
||||
/**
|
||||
* When the server is full and someone with this permission joins the server, someone will be kicked.
|
||||
*/
|
||||
IS_VIP("authme.vip", DefaultPermission.OP_ONLY),
|
||||
IS_VIP("authme.vip", DefaultPermission.NOT_ALLOWED),
|
||||
|
||||
/**
|
||||
* Permission to be able to register multiple accounts.
|
||||
@ -34,7 +34,12 @@ public enum PlayerStatePermission implements PermissionNode {
|
||||
/**
|
||||
* Permission to bypass the GeoIp country code check.
|
||||
*/
|
||||
BYPASS_COUNTRY_CHECK("authme.bypasscountrycheck", DefaultPermission.NOT_ALLOWED);
|
||||
BYPASS_COUNTRY_CHECK("authme.bypasscountrycheck", DefaultPermission.NOT_ALLOWED),
|
||||
|
||||
/**
|
||||
* Permission to send chat messages before being logged in.
|
||||
*/
|
||||
ALLOW_CHAT_BEFORE_LOGIN("authme.allowchatbeforelogin", DefaultPermission.NOT_ALLOWED);
|
||||
|
||||
/**
|
||||
* The permission node.
|
||||
@ -42,7 +47,7 @@ public enum PlayerStatePermission implements PermissionNode {
|
||||
private String node;
|
||||
|
||||
/**
|
||||
* The default permission level
|
||||
* The default permission level.
|
||||
*/
|
||||
private DefaultPermission defaultPermission;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package fr.xephi.authme.permission.handlers;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.permission.PermissionsSystemType;
|
||||
import me.lucko.luckperms.LuckPerms;
|
||||
@ -41,13 +42,8 @@ public class LuckPermsHandler implements PermissionHandler {
|
||||
}
|
||||
|
||||
private void saveUser(User user) {
|
||||
luckPermsApi.getStorage().saveUser(user)
|
||||
.thenAcceptAsync(wasSuccessful -> {
|
||||
if (!wasSuccessful) {
|
||||
return;
|
||||
}
|
||||
user.refreshPermissions();
|
||||
}, luckPermsApi.getStorage().getAsyncExecutor());
|
||||
luckPermsApi.getUserManager().saveUser(user)
|
||||
.thenAcceptAsync(wasSuccessful -> user.refreshCachedData());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -62,7 +58,8 @@ public class LuckPermsHandler implements PermissionHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
DataMutateResult result = user.setPermissionUnchecked(luckPermsApi.getNodeFactory().makeGroupNode(newGroup).build());
|
||||
DataMutateResult result = user.setPermission(
|
||||
luckPermsApi.getNodeFactory().makeGroupNode(newGroup).build());
|
||||
if (result == DataMutateResult.FAIL) {
|
||||
return false;
|
||||
}
|
||||
@ -82,6 +79,8 @@ public class LuckPermsHandler implements PermissionHandler {
|
||||
public boolean hasPermissionOffline(String name, PermissionNode node) {
|
||||
User user = luckPermsApi.getUser(name);
|
||||
if (user == null) {
|
||||
ConsoleLogger.warning("LuckPermsHandler: tried to check permission for offline user "
|
||||
+ name + " but it isn't loaded!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -97,6 +96,8 @@ public class LuckPermsHandler implements PermissionHandler {
|
||||
public boolean isInGroup(OfflinePlayer player, String group) {
|
||||
User user = luckPermsApi.getUser(player.getName());
|
||||
if (user == null) {
|
||||
ConsoleLogger.warning("LuckPermsHandler: tried to check group for offline user "
|
||||
+ player.getName() + " but it isn't loaded!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -111,6 +112,8 @@ public class LuckPermsHandler implements PermissionHandler {
|
||||
public boolean removeFromGroup(OfflinePlayer player, String group) {
|
||||
User user = luckPermsApi.getUser(player.getName());
|
||||
if (user == null) {
|
||||
ConsoleLogger.warning("LuckPermsHandler: tried to remove group for offline user "
|
||||
+ player.getName() + " but it isn't loaded!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -120,7 +123,7 @@ public class LuckPermsHandler implements PermissionHandler {
|
||||
}
|
||||
|
||||
Node groupNode = luckPermsApi.getNodeFactory().makeGroupNode(permissionGroup).build();
|
||||
boolean result = user.unsetPermissionUnchecked(groupNode) != DataMutateResult.FAIL;
|
||||
boolean result = user.unsetPermission(groupNode) != DataMutateResult.FAIL;
|
||||
|
||||
luckPermsApi.cleanupUser(user);
|
||||
return result;
|
||||
@ -130,6 +133,8 @@ public class LuckPermsHandler implements PermissionHandler {
|
||||
public boolean setGroup(OfflinePlayer player, String group) {
|
||||
User user = luckPermsApi.getUser(player.getName());
|
||||
if (user == null) {
|
||||
ConsoleLogger.warning("LuckPermsHandler: tried to set group for offline user "
|
||||
+ player.getName() + " but it isn't loaded!");
|
||||
return false;
|
||||
}
|
||||
Group permissionGroup = luckPermsApi.getGroup(group);
|
||||
@ -137,7 +142,7 @@ public class LuckPermsHandler implements PermissionHandler {
|
||||
return false;
|
||||
}
|
||||
Node groupNode = luckPermsApi.getNodeFactory().makeGroupNode(permissionGroup).build();
|
||||
DataMutateResult result = user.setPermissionUnchecked(groupNode);
|
||||
DataMutateResult result = user.setPermission(groupNode);
|
||||
if (result == DataMutateResult.FAIL) {
|
||||
return false;
|
||||
}
|
||||
@ -152,6 +157,8 @@ public class LuckPermsHandler implements PermissionHandler {
|
||||
public List<String> getGroups(OfflinePlayer player) {
|
||||
User user = luckPermsApi.getUser(player.getName());
|
||||
if (user == null) {
|
||||
ConsoleLogger.warning("LuckPermsHandler: tried to get groups for offline user "
|
||||
+ player.getName() + " but it isn't loaded!");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@ -182,22 +189,21 @@ public class LuckPermsHandler implements PermissionHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadUserData(UUID uuid) {
|
||||
public void loadUserData(UUID uuid) throws PermissionLoadUserException {
|
||||
try {
|
||||
luckPermsApi.getStorage().loadUser(uuid).get(5, TimeUnit.SECONDS);
|
||||
luckPermsApi.getUserManager().loadUser(uuid).get(5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
e.printStackTrace();
|
||||
throw new PermissionLoadUserException("Unable to load the permission data of the user " + uuid, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadUserData(String name) {
|
||||
public void loadUserData(String name) throws PermissionLoadUserException {
|
||||
try {
|
||||
UUID uuid = luckPermsApi.getStorage().getUUID(name).get(5, TimeUnit.SECONDS);
|
||||
loadUserData(uuid);
|
||||
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
e.printStackTrace();
|
||||
throw new PermissionLoadUserException("Unable to load the permission data of the user " + name, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -107,10 +107,9 @@ public interface PermissionHandler {
|
||||
*/
|
||||
PermissionsSystemType getPermissionSystem();
|
||||
|
||||
default void loadUserData(UUID uuid) {
|
||||
default void loadUserData(UUID uuid) throws PermissionLoadUserException {
|
||||
}
|
||||
|
||||
default void loadUserData(String name) {
|
||||
default void loadUserData(String name) throws PermissionLoadUserException {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
package fr.xephi.authme.permission.handlers;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Exception thrown when a {@link PermissionHandler#loadUserData(UUID uuid)} request fails.
|
||||
*/
|
||||
public class PermissionLoadUserException extends Exception {
|
||||
|
||||
public PermissionLoadUserException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -12,7 +12,6 @@ import fr.xephi.authme.process.register.executors.RegistrationMethod;
|
||||
import fr.xephi.authme.process.register.executors.RegistrationParameters;
|
||||
import fr.xephi.authme.process.unregister.AsynchronousUnregister;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
|
@ -4,8 +4,10 @@ import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.events.EmailChangedEvent;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.AsynchronousProcess;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.service.bungeecord.BungeeSender;
|
||||
@ -35,6 +37,9 @@ public class AsyncAddEmail implements AsynchronousProcess {
|
||||
@Inject
|
||||
private BungeeSender bungeeSender;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
AsyncAddEmail() { }
|
||||
|
||||
/**
|
||||
@ -57,6 +62,13 @@ public class AsyncAddEmail implements AsynchronousProcess {
|
||||
} else if (!validationService.isEmailFreeForRegistration(email, player)) {
|
||||
service.send(player, MessageKey.EMAIL_ALREADY_USED_ERROR);
|
||||
} else {
|
||||
EmailChangedEvent event = bukkitService.createAndCallEvent(isAsync
|
||||
-> new EmailChangedEvent(player, null, email, isAsync));
|
||||
if (event.isCancelled()) {
|
||||
ConsoleLogger.info("Could not add email to player '" + player + "' – event was cancelled");
|
||||
service.send(player, MessageKey.EMAIL_ADD_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
auth.setEmail(email);
|
||||
if (dataSource.updateEmail(auth)) {
|
||||
playerCache.updatePlayer(auth);
|
||||
|
@ -1,10 +1,13 @@
|
||||
package fr.xephi.authme.process.email;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.events.EmailChangedEvent;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.AsynchronousProcess;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.service.bungeecord.BungeeSender;
|
||||
@ -32,6 +35,9 @@ public class AsyncChangeEmail implements AsynchronousProcess {
|
||||
|
||||
@Inject
|
||||
private BungeeSender bungeeSender;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
AsyncChangeEmail() { }
|
||||
|
||||
@ -57,14 +63,22 @@ public class AsyncChangeEmail implements AsynchronousProcess {
|
||||
} else if (!validationService.isEmailFreeForRegistration(newEmail, player)) {
|
||||
service.send(player, MessageKey.EMAIL_ALREADY_USED_ERROR);
|
||||
} else {
|
||||
saveNewEmail(auth, player, newEmail);
|
||||
saveNewEmail(auth, player, oldEmail, newEmail);
|
||||
}
|
||||
} else {
|
||||
outputUnloggedMessage(player);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveNewEmail(PlayerAuth auth, Player player, String newEmail) {
|
||||
private void saveNewEmail(PlayerAuth auth, Player player, String oldEmail, String newEmail) {
|
||||
EmailChangedEvent event = bukkitService.createAndCallEvent(isAsync
|
||||
-> new EmailChangedEvent(player, oldEmail, newEmail, isAsync));
|
||||
if (event.isCancelled()) {
|
||||
ConsoleLogger.info("Could not change email for player '" + player + "' – event was cancelled");
|
||||
service.send(player, MessageKey.EMAIL_CHANGE_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
|
||||
auth.setEmail(newEmail);
|
||||
if (dataSource.updateEmail(auth)) {
|
||||
playerCache.updatePlayer(auth);
|
||||
|
@ -18,9 +18,9 @@ import fr.xephi.authme.settings.commandconfig.CommandManager;
|
||||
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.InternetProtocolUtils;
|
||||
import fr.xephi.authme.util.PlayerUtils;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
@ -139,7 +139,7 @@ public class AsynchronousJoin implements AsynchronousProcess {
|
||||
|
||||
private void handlePlayerWithUnmetNameRestriction(Player player, String ip) {
|
||||
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {
|
||||
player.kickPlayer(service.retrieveSingleMessage(MessageKey.NOT_OWNER_ERROR));
|
||||
player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.NOT_OWNER_ERROR));
|
||||
if (service.getProperty(RestrictionSettings.BAN_UNKNOWN_IP)) {
|
||||
server.banIP(ip);
|
||||
}
|
||||
@ -184,12 +184,11 @@ public class AsynchronousJoin implements AsynchronousProcess {
|
||||
private boolean validatePlayerCountForIp(final Player player, String ip) {
|
||||
if (service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP) > 0
|
||||
&& !service.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)
|
||||
&& !"127.0.0.1".equalsIgnoreCase(ip)
|
||||
&& !"localhost".equalsIgnoreCase(ip)
|
||||
&& !InternetProtocolUtils.isLoopbackAddress(ip)
|
||||
&& countOnlinePlayersByIp(ip) > service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP)) {
|
||||
|
||||
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(
|
||||
() -> player.kickPlayer(service.retrieveSingleMessage(MessageKey.SAME_IP_ONLINE)));
|
||||
() -> player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.SAME_IP_ONLINE)));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -6,6 +6,8 @@ import fr.xephi.authme.data.TempbanManager;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.data.captcha.LoginCaptchaManager;
|
||||
import fr.xephi.authme.data.limbo.LimboMessageType;
|
||||
import fr.xephi.authme.data.limbo.LimboPlayerState;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.events.AuthMeAsyncPreLoginEvent;
|
||||
@ -28,6 +30,7 @@ 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.RestrictionSettings;
|
||||
import fr.xephi.authme.util.InternetProtocolUtils;
|
||||
import fr.xephi.authme.util.PlayerUtils;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
import org.bukkit.ChatColor;
|
||||
@ -90,7 +93,13 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
||||
public void login(Player player, String password) {
|
||||
PlayerAuth auth = getPlayerAuth(player);
|
||||
if (auth != null && checkPlayerInfo(player, auth, password)) {
|
||||
performLogin(player, auth);
|
||||
if (auth.getTotpKey() != null) {
|
||||
limboService.resetMessageTask(player, LimboMessageType.TOTP_CODE);
|
||||
limboService.getLimboPlayer(player.getName()).setState(LimboPlayerState.TOTP_REQUIRED);
|
||||
// TODO #1141: Check if we should check limbo state before processing password
|
||||
} else {
|
||||
performLogin(player, auth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +134,7 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
||||
if (auth == null) {
|
||||
service.send(player, MessageKey.UNKNOWN_USER);
|
||||
// Recreate the message task to immediately send the message again as response
|
||||
limboService.resetMessageTask(player, false);
|
||||
limboService.resetMessageTask(player, LimboMessageType.REGISTER);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -197,7 +206,7 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
||||
tempbanManager.tempbanPlayer(player);
|
||||
} else if (service.getProperty(RestrictionSettings.KICK_ON_WRONG_PASSWORD)) {
|
||||
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(
|
||||
() -> player.kickPlayer(service.retrieveSingleMessage(MessageKey.WRONG_PASSWORD)));
|
||||
() -> player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.WRONG_PASSWORD)));
|
||||
} else {
|
||||
service.send(player, MessageKey.WRONG_PASSWORD);
|
||||
|
||||
@ -218,7 +227,7 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
||||
* @param player the player to log in
|
||||
* @param auth the associated PlayerAuth object
|
||||
*/
|
||||
private void performLogin(Player player, PlayerAuth auth) {
|
||||
public void performLogin(Player player, PlayerAuth auth) {
|
||||
if (player.isOnline()) {
|
||||
final boolean isFirstLogin = (auth.getLastLogin() == null);
|
||||
|
||||
@ -317,8 +326,7 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
||||
// Do not perform the check if player has multiple accounts permission or if IP is localhost
|
||||
if (service.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP) <= 0
|
||||
|| service.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)
|
||||
|| "127.0.0.1".equalsIgnoreCase(ip)
|
||||
|| "localhost".equalsIgnoreCase(ip)) {
|
||||
|| InternetProtocolUtils.isLoopbackAddress(ip)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -4,17 +4,19 @@ import ch.jalu.injector.factory.SingletonStore;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.events.AuthMeAsyncPreRegisterEvent;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.process.AsynchronousProcess;
|
||||
import fr.xephi.authme.process.register.executors.RegistrationExecutor;
|
||||
import fr.xephi.authme.process.register.executors.RegistrationMethod;
|
||||
import fr.xephi.authme.process.register.executors.RegistrationParameters;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.bungeecord.BungeeSender;
|
||||
import fr.xephi.authme.service.bungeecord.MessageType;
|
||||
import fr.xephi.authme.settings.properties.RegistrationSettings;
|
||||
import fr.xephi.authme.settings.properties.RestrictionSettings;
|
||||
import fr.xephi.authme.util.InternetProtocolUtils;
|
||||
import fr.xephi.authme.util.PlayerUtils;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@ -33,9 +35,9 @@ public class AsyncRegister implements AsynchronousProcess {
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
@Inject
|
||||
private CommonService service;
|
||||
private BukkitService bukkitService;
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
private CommonService service;
|
||||
@Inject
|
||||
private SingletonStore<RegistrationExecutor> registrationExecutorFactory;
|
||||
@Inject
|
||||
@ -47,9 +49,9 @@ public class AsyncRegister implements AsynchronousProcess {
|
||||
/**
|
||||
* Performs the registration process for the given player.
|
||||
*
|
||||
* @param variant the registration method
|
||||
* @param variant the registration method
|
||||
* @param parameters the parameters
|
||||
* @param <P> parameters type
|
||||
* @param <P> parameters type
|
||||
*/
|
||||
public <P extends RegistrationParameters> void register(RegistrationMethod<P> variant, P parameters) {
|
||||
if (preRegisterCheck(parameters.getPlayer())) {
|
||||
@ -60,6 +62,13 @@ public class AsyncRegister implements AsynchronousProcess {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player is able to register, in that case the {@link AuthMeAsyncPreRegisterEvent} is invoked.
|
||||
*
|
||||
* @param player the player which is trying to register.
|
||||
*
|
||||
* @return true if the checks are successful and the event hasn't marked the action as denied, false otherwise.
|
||||
*/
|
||||
private boolean preRegisterCheck(Player player) {
|
||||
final String name = player.getName().toLowerCase();
|
||||
if (playerCache.isAuthenticated(name)) {
|
||||
@ -73,6 +82,12 @@ public class AsyncRegister implements AsynchronousProcess {
|
||||
return false;
|
||||
}
|
||||
|
||||
AuthMeAsyncPreRegisterEvent event = bukkitService.createAndCallEvent(
|
||||
isAsync -> new AuthMeAsyncPreRegisterEvent(player, isAsync));
|
||||
if (!event.canRegister()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isPlayerIpAllowedToRegister(player);
|
||||
}
|
||||
|
||||
@ -80,11 +95,11 @@ public class AsyncRegister implements AsynchronousProcess {
|
||||
* Executes the registration.
|
||||
*
|
||||
* @param parameters the registration parameters
|
||||
* @param executor the executor to perform the registration process with
|
||||
* @param <P> registration params type
|
||||
* @param executor the executor to perform the registration process with
|
||||
* @param <P> registration params type
|
||||
*/
|
||||
private <P extends RegistrationParameters>
|
||||
void executeRegistration(P parameters, RegistrationExecutor<P> executor) {
|
||||
void executeRegistration(P parameters, RegistrationExecutor<P> executor) {
|
||||
PlayerAuth auth = executor.buildPlayerAuth(parameters);
|
||||
if (database.saveAuth(auth)) {
|
||||
executor.executePostPersistAction(parameters);
|
||||
@ -98,15 +113,15 @@ public class AsyncRegister implements AsynchronousProcess {
|
||||
* Checks whether the registration threshold has been exceeded for the given player's IP address.
|
||||
*
|
||||
* @param player the player to check
|
||||
*
|
||||
* @return true if registration may take place, false otherwise (IP check failed)
|
||||
*/
|
||||
private boolean isPlayerIpAllowedToRegister(Player player) {
|
||||
final int maxRegPerIp = service.getProperty(RestrictionSettings.MAX_REGISTRATION_PER_IP);
|
||||
final String ip = PlayerUtils.getPlayerIp(player);
|
||||
if (maxRegPerIp > 0
|
||||
&& !"127.0.0.1".equalsIgnoreCase(ip)
|
||||
&& !"localhost".equalsIgnoreCase(ip)
|
||||
&& !permissionsManager.hasPermission(player, ALLOW_MULTIPLE_ACCOUNTS)) {
|
||||
&& !InternetProtocolUtils.isLoopbackAddress(ip)
|
||||
&& !service.hasPermission(player, ALLOW_MULTIPLE_ACCOUNTS)) {
|
||||
List<String> otherAccounts = database.getAllAuthsByIp(ip);
|
||||
if (otherAccounts.size() >= maxRegPerIp) {
|
||||
service.send(player, MessageKey.MAX_REGISTER_EXCEEDED, Integer.toString(maxRegPerIp),
|
||||
|
@ -2,17 +2,24 @@ package fr.xephi.authme.process.register;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.events.RegisterEvent;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.SynchronousProcess;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.util.PlayerUtils;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
||||
/**
|
||||
* Performs synchronous tasks after a successful {@link RegistrationType#EMAIL email registration}.
|
||||
*/
|
||||
public class ProcessSyncEmailRegister implements SynchronousProcess {
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private CommonService service;
|
||||
|
||||
@ -22,11 +29,17 @@ public class ProcessSyncEmailRegister implements SynchronousProcess {
|
||||
ProcessSyncEmailRegister() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs sync tasks for a player which has just registered by email.
|
||||
*
|
||||
* @param player the recently registered player
|
||||
*/
|
||||
public void processEmailRegister(Player player) {
|
||||
service.send(player, MessageKey.ACCOUNT_NOT_ACTIVATED);
|
||||
limboService.replaceTasksAfterRegistration(player);
|
||||
|
||||
player.saveData();
|
||||
bukkitService.callEvent(new RegisterEvent(player));
|
||||
ConsoleLogger.fine(player.getName() + " registered " + PlayerUtils.getPlayerIp(player));
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,10 @@ package fr.xephi.authme.process.register;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.events.RegisterEvent;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.SynchronousProcess;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.bungeecord.BungeeSender;
|
||||
import fr.xephi.authme.settings.commandconfig.CommandManager;
|
||||
@ -15,6 +17,7 @@ import org.bukkit.entity.Player;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Performs synchronous tasks after a successful {@link RegistrationType#PASSWORD password registration}.
|
||||
*/
|
||||
public class ProcessSyncPasswordRegister implements SynchronousProcess {
|
||||
|
||||
@ -30,6 +33,9 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess {
|
||||
@Inject
|
||||
private CommandManager commandManager;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
ProcessSyncPasswordRegister() {
|
||||
}
|
||||
|
||||
@ -46,6 +52,11 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a player having registered with a password.
|
||||
*
|
||||
* @param player the newly registered player
|
||||
*/
|
||||
public void processPasswordRegister(Player player) {
|
||||
service.send(player, MessageKey.REGISTER_SUCCESS);
|
||||
|
||||
@ -54,11 +65,12 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess {
|
||||
}
|
||||
|
||||
player.saveData();
|
||||
bukkitService.callEvent(new RegisterEvent(player));
|
||||
ConsoleLogger.fine(player.getName() + " registered " + PlayerUtils.getPlayerIp(player));
|
||||
|
||||
// Kick Player after Registration is enabled, kick the player
|
||||
if (service.getProperty(RegistrationSettings.FORCE_KICK_AFTER_REGISTER)) {
|
||||
player.kickPlayer(service.retrieveSingleMessage(MessageKey.REGISTER_SUCCESS));
|
||||
player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.REGISTER_SUCCESS));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.mail.EmailService;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.process.SyncProcessManager;
|
||||
import fr.xephi.authme.security.PasswordSecurity;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
@ -25,9 +24,6 @@ import static fr.xephi.authme.settings.properties.EmailSettings.RECOVERY_PASSWOR
|
||||
*/
|
||||
class EmailRegisterExecutor implements RegistrationExecutor<EmailRegisterParams> {
|
||||
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@ -46,7 +42,7 @@ class EmailRegisterExecutor implements RegistrationExecutor<EmailRegisterParams>
|
||||
@Override
|
||||
public boolean isRegistrationAdmitted(EmailRegisterParams params) {
|
||||
final int maxRegPerEmail = commonService.getProperty(EmailSettings.MAX_REG_PER_EMAIL);
|
||||
if (maxRegPerEmail > 0 && !permissionsManager.hasPermission(params.getPlayer(), ALLOW_MULTIPLE_ACCOUNTS)) {
|
||||
if (maxRegPerEmail > 0 && !commonService.hasPermission(params.getPlayer(), ALLOW_MULTIPLE_ACCOUNTS)) {
|
||||
int otherAccounts = dataSource.countAuthsByEmail(params.getEmail());
|
||||
if (otherAccounts >= maxRegPerEmail) {
|
||||
commonService.send(params.getPlayer(), MessageKey.MAX_REGISTER_EXCEEDED,
|
||||
|
@ -1,6 +1,7 @@
|
||||
package fr.xephi.authme.security;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
@ -78,6 +79,20 @@ public final class HashUtils {
|
||||
return hash.length() > 3 && hash.substring(0, 2).equals("$2");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the two strings are equal to each other in a time-constant manner.
|
||||
* This helps to avoid timing side channel attacks,
|
||||
* cf. <a href="https://github.com/AuthMe/AuthMeReloaded/issues/1561">issue #1561</a>.
|
||||
*
|
||||
* @param string1 first string
|
||||
* @param string2 second string
|
||||
* @return true if the strings are equal to each other, false otherwise
|
||||
*/
|
||||
public static boolean isEqual(String string1, String string2) {
|
||||
return MessageDigest.isEqual(
|
||||
string1.getBytes(StandardCharsets.UTF_8), string2.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash the message with the given algorithm and return the hash in its hexadecimal notation.
|
||||
*
|
||||
|
@ -8,7 +8,7 @@ import fr.xephi.authme.security.crypts.description.SaltType;
|
||||
import fr.xephi.authme.security.crypts.description.Usage;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.HooksSettings;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import fr.xephi.authme.util.ExceptionUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@ -39,7 +39,7 @@ public class BCrypt implements EncryptionMethod {
|
||||
try {
|
||||
return HashUtils.isValidBcryptHash(hash.getHash()) && BCryptService.checkpw(password, hash.getHash());
|
||||
} catch (IllegalArgumentException e) {
|
||||
ConsoleLogger.warning("Bcrypt checkpw() returned " + StringUtils.formatException(e));
|
||||
ConsoleLogger.warning("Bcrypt checkpw() returned " + ExceptionUtils.formatException(e));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package fr.xephi.authme.security.crypts;
|
||||
import fr.xephi.authme.security.crypts.description.Recommendation;
|
||||
import fr.xephi.authme.security.crypts.description.Usage;
|
||||
|
||||
import static fr.xephi.authme.security.HashUtils.isEqual;
|
||||
|
||||
@Recommendation(Usage.RECOMMENDED)
|
||||
public class BCrypt2y extends HexSaltedMethod {
|
||||
|
||||
@ -23,7 +25,7 @@ public class BCrypt2y extends HexSaltedMethod {
|
||||
// The salt is the first 29 characters of the hash
|
||||
|
||||
String salt = hash.substring(0, 29);
|
||||
return hash.equals(computeHash(password, salt, null));
|
||||
return isEqual(hash, computeHash(password, salt, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2,12 +2,12 @@ package fr.xephi.authme.security.crypts;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.security.HashUtils;
|
||||
import fr.xephi.authme.util.RandomStringUtils;
|
||||
import fr.xephi.authme.security.crypts.description.HasSalt;
|
||||
import fr.xephi.authme.security.crypts.description.Recommendation;
|
||||
import fr.xephi.authme.security.crypts.description.SaltType;
|
||||
import fr.xephi.authme.security.crypts.description.Usage;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import fr.xephi.authme.util.ExceptionUtils;
|
||||
import fr.xephi.authme.util.RandomStringUtils;
|
||||
|
||||
|
||||
/**
|
||||
@ -37,7 +37,7 @@ public class Ipb4 implements EncryptionMethod {
|
||||
try {
|
||||
return HashUtils.isValidBcryptHash(hash.getHash()) && BCryptService.checkpw(password, hash.getHash());
|
||||
} catch (IllegalArgumentException e) {
|
||||
ConsoleLogger.warning("Bcrypt checkpw() returned " + StringUtils.formatException(e));
|
||||
ConsoleLogger.warning("Bcrypt checkpw() returned " + ExceptionUtils.formatException(e));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import fr.xephi.authme.security.HashUtils;
|
||||
import fr.xephi.authme.security.crypts.description.Recommendation;
|
||||
import fr.xephi.authme.security.crypts.description.Usage;
|
||||
|
||||
import static fr.xephi.authme.security.HashUtils.isEqual;
|
||||
|
||||
@Recommendation(Usage.ACCEPTABLE)
|
||||
public class Joomla extends HexSaltedMethod {
|
||||
|
||||
@ -16,7 +18,7 @@ public class Joomla extends HexSaltedMethod {
|
||||
public boolean comparePassword(String password, HashedPassword hashedPassword, String unusedName) {
|
||||
String hash = hashedPassword.getHash();
|
||||
String[] hashParts = hash.split(":");
|
||||
return hashParts.length == 2 && hash.equals(computeHash(password, hashParts[1], null));
|
||||
return hashParts.length == 2 && isEqual(hash, computeHash(password, hashParts[1], null));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user