Merge branch 'master' into feargames

This commit is contained in:
Gabriele C 2021-12-12 18:58:18 +01:00
commit cd6a4b72e6
61 changed files with 1131 additions and 297 deletions

View File

@ -1,34 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'Type: bug'
assignees: ''
---
#### Before reporting an issue make sure you are running the latest build of the plugin and checked for duplicate issues!
### What behaviour is observed:
What happened?
### What behaviour is expected:
What did you expect?
### Steps/models to reproduce:
The actions that cause the issue
### Plugin list:
This can be found by running `/pl`
### Environment description
Standalone server/Bungeecord network, SQLite/MySql, ...
### AuthMe build number:
This can be found by running `/authme version`
### Error Log:
Pastebin/Hastebin/Gist link of the error log or stacktrace (if any)
### Configuration:
Pastebin/Hastebin/Gist link of your config.yml file (remember to delete any sensitive data)

83
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@ -0,0 +1,83 @@
name: Bug report
description: Create a report to help us improve
labels: 'Type: bug'
body:
- type: markdown
attributes:
value: |
Before reporting an issue make sure you are running the latest build of the plugin and checked for duplicate issues!
- type: textarea
attributes:
label: What behaviour is observed?
description: A clear and concise description of what the behavior is.
validations:
required: true
- type: textarea
attributes:
label: Expected behaviour
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: To Reproduce
description: Steps to reproduce this behaviour
placeholder: |
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error
validations:
required: true
- type: textarea
attributes:
label: Plugin list
description: This can be found by running `/pl`
validations:
required: true
- type: dropdown
attributes:
label: Server Implementation
description: Which server implementation are you using?
multiple: false
options:
- Standalone server (no proxy)
- BungeeCord
validations:
required: true
- type: dropdown
attributes:
label: Database Implementation
description: Which database implementation are you using?
multiple: false
options:
- SQLite
- MySQL
validations:
required: true
- type: input
attributes:
label: AuthMe Version
description: What version of AuthMe are you running? (`/authme version`)
validations:
required: true
- type: input
attributes:
label: Error log (if applicable)
description: If you are reporting a console error, upload any relevant log excerpts to either https://paste.gg/ or https://gist.github.com, save and the paste the link in this box.
- type: input
attributes:
label: Configuration
description: Link of your config.yml file (remember to delete any sensitive data), upload any relevant log excerpts to either https://paste.gg/ or https://gist.github.com, save and the paste the link in this box.
validations:
required: true

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1 @@
blank_issues_enabled: false

View File

@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for AuthMe
title: ''
labels: 'Type: enhancement'
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -0,0 +1,28 @@
name: Feature request
description: Suggest an idea for AuthMe
labels: 'Type: enhancement'
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this feature request for AuthMe! Fill out the following form to your best ability to help us understand your feature request and greately improve the change of it getting added.
- type: textarea
attributes:
label: What feature do you want to see added?
description: A clear and concise description of your feature request.
validations:
required: true
- type: textarea
attributes:
label: Are there any alternatives?
description: List any alternatives you might have tried
validations:
required: true
- type: textarea
attributes:
label: Anything else?
description: You can provide additional context below.

View File

@ -7,11 +7,6 @@ updates:
open-pull-requests-limit: 10
ignore:
- dependency-name: com.google.code.gson:gson
versions:
- "> 2.2.4"
- dependency-name: com.google.guava:guava
versions:
- "> 17.0"
- dependency-name: org.apache.logging.log4j:log4j-core
versions:
- "> 2.5, < 3"
- dependency-name: mysql:mysql-connector-java

View File

@ -2,9 +2,12 @@ sudo: false
dist: trusty
language: java
jdk:
- oraclejdk8
- openjdk11
- openjdk16
before_install:
- wget https://downloads.apache.org/maven/maven-3/3.8.4/binaries/apache-maven-3.8.4-bin.tar.gz
- tar xzvf apache-maven-3.8.4-bin.tar.gz
- export PATH=`pwd`/apache-maven-3.8.4/bin:$PATH
- mvn -v
cache:
directories:
- '$HOME/.m2/repository'

View File

@ -1,13 +1,14 @@
# AuthMeReloaded
**"The best authentication plugin for the Bukkit modding API!"**
<img src="wallpaper.png?raw=true" alt="AuthMeLogo" style="width: 250px;"/>
<img src="wallpaper.png?raw=true" alt="AuthMeLogo"/>
| 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:** | [![Build Status](https://travis-ci.org/AuthMe/AuthMeReloaded.svg?branch=master)](https://travis-ci.org/AuthMe/AuthMeReloaded) |
| **Other CIs:** | [![Build Status](https://www.travis-ci.com/AuthMe/AuthMeReloaded.svg?branch=master)](https://www.travis-ci.com/AuthMe/AuthMeReloaded) |
## Description
@ -24,16 +25,17 @@ You can also create your own translation file and, if you want, you can share it
#### Features:
<ul>
<li><strong>E-Mail Recovery System !!!</strong></li>
<li><strong>E-Mail Recovery System!</strong></li>
<li>Username spoofing protection.</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>
<li>Two-factor (2FA) support!</li>
<li>Session Login!</li>
<li>Editable translations and messages!</li>
<li><strong>MySQL and SQLite Backend support!</strong></li>
<li>Supported password encryption algorithms: SHA256, BCRYPT, PBKDF2, <a href="https://github.com/CypherX/xAuth/wiki/Password-Hashing">xAuth</a></li>
<li>Supported password encryption algorithms: SHA256, ARGON2, BCRYPT, PBKDF2, <a href="https://github.com/CypherX/xAuth/wiki/Password-Hashing">xAuth</a></li>
<li>Supported alternative registration methods:<br>
<ul>
<li>PHPBB, VBulletin: PHPBB - MD5VB</li>
@ -83,9 +85,9 @@ You can also create your own translation file and, if you want, you can share it
- **Support:**
- [GitHub issue tracker](https://github.com/AuthMe/AuthMeReloaded/issues)
- [Discord](https://discord.gg/Vn9eCyE)
- [BukkitDev page](https://dev.bukkit.org/projects/authme-reloaded)
- [Spigot page](https://www.spigotmc.org/resources/authmereloaded.6269/)
- [Discord](https://discord.gg/Vn9eCyE)
- **Dev resources:**
- <a href="https://ci.codemc.org/job/AuthMe/job/AuthMeReloaded/javadoc/">JavaDocs</a>
@ -114,7 +116,7 @@ You can also create your own translation file and, if you want, you can share it
## Requirements
##### Compiling requirements:
>- JDK 1.8
>- JDK 8 (JDK 17 is recommended)
>- Maven
>- Git/Github (Optional)
@ -123,8 +125,8 @@ You can also create your own translation file and, if you want, you can share it
>- 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>
>- Java 8 (Java 11 is recommended)
>- Paper or Spigot (1.8.X and up)<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)

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Sat May 16 15:10:08 CEST 2020. See docs/commands/commands.tpl.md -->
<!-- File auto-generated on Sun Apr 04 21:31:42 CEST 2021. 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 `< >`
@ -105,6 +105,7 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
<br />Requires `authme.player.security.verificationcode`
- **/verification help** [query]: View detailed help for /verification commands.
---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sat May 16 15:10:08 CEST 2020
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 04 21:31:42 CEST 2021

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Tue Jan 21 10:33:12 CET 2020. See docs/config/config.tpl.md -->
<!-- File auto-generated on Sun Nov 28 16:47:07 CET 2021. 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,
@ -28,6 +28,11 @@ DataSource:
mySQLUsername: authme
# Password to connect to the MySQL database
mySQLPassword: '12345'
# Driver Name of the MySQL database.
# Built-in drivers:
# MySQL: 'fr.xephi.authme.libs.com.mysql.cj.jdbc.Driver'
# MariaDB: 'fr.xephi.authme.libs.org.mariadb.jdbc.Driver'
mySQLDriverClassName: fr.xephi.authme.libs.com.mysql.cj.jdbc.Driver
# Database Name, use with converters or as SQLITE database name
mySQLDatabase: authme
# Table of the database
@ -87,7 +92,7 @@ ExternalBoardOptions:
# Other MySQL columns where we need to put the username (case-sensitive)
mySQLOtherUsernameColumns: []
# How much log2 rounds needed in BCrypt (do not change if you do not know what it does)
bCryptLog2Round: 10
bCryptLog2Round: 12
# phpBB table prefix defined during the phpBB installation process
phpbbTablePrefix: phpbb_
# phpBB activated group ID; 2 is the default registered group defined by phpBB
@ -138,8 +143,9 @@ settings:
# Allowed commands for unauthenticated players
allowCommands:
- /login
- /register
- /log
- /l
- /register
- /reg
- /email
- /captcha
@ -584,4 +590,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 Tue Jan 21 10:33:12 CET 2020
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Nov 28 16:47:07 CET 2021

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Fri Apr 19 17:16:06 CEST 2019. See docs/hashmethods/hash_algorithms.tpl.md -->
<!-- File auto-generated on Sun Apr 04 21:31:44 CEST 2021. See docs/hashmethods/hash_algorithms.tpl.md -->
## Hash Algorithms
AuthMe supports the following hash algorithms for storing your passwords safely.
@ -80,4 +80,4 @@ or bad.
---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Apr 19 17:16:06 CEST 2019
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 04 21:31:44 CEST 2021

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Fri Aug 02 16:25:54 CEST 2019. See docs/permissions/permission_nodes.tpl.md -->
<!-- File auto-generated on Sun Apr 04 21:31:44 CEST 2021. 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.
@ -73,4 +73,4 @@ The following are the permission nodes that are currently supported by the lates
---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Aug 02 16:25:54 CEST 2019
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 04 21:31:44 CEST 2021

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Sat May 16 15:10:13 CEST 2020. See docs/translations/translations.tpl.md -->
<!-- File auto-generated on Sun Apr 04 21:31:44 CEST 2021. See docs/translations/translations.tpl.md -->
# AuthMe Translations
The following translations are available in AuthMe. Set `messagesLanguage` to the language code
@ -10,12 +10,12 @@ Code | Language | Translated | &nbsp;
[en](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_en.yml) | English | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
[br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 79% | <img src="https://via.placeholder.com/79x7/bb9900?text=%20" alt="79" />
[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
[eo](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eo.yml) | Esperanto | 79% | <img src="https://via.placeholder.com/79x7/bb9900?text=%20" alt="79" />
[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
[et](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_et.yml) | Estonian | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 42% | <img src="https://via.placeholder.com/42x7/aa5500?text=%20" alt="42" />
[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 45% | <img src="https://via.placeholder.com/45x7/aa5500?text=%20" alt="45" />
[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 48% | <img src="https://via.placeholder.com/48x7/aa5500?text=%20" alt="48" />
@ -27,7 +27,7 @@ Code | Language | Translated | &nbsp;
[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
[ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 79% | <img src="https://via.placeholder.com/79x7/bb9900?text=%20" alt="79" />
[ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
[si](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_si.yml) | Slovenian | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 79% | <img src="https://via.placeholder.com/79x7/bb9900?text=%20" alt="79" />
@ -40,6 +40,7 @@ Code | Language | Translated | &nbsp;
[zhmc](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhmc.yml) | Chinese (Macau) | 64% | <img src="https://via.placeholder.com/64x7/bb7700?text=%20" alt="64" />
[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 86% | <img src="https://via.placeholder.com/86x7/99bb22?text=%20" alt="86" />
---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sat May 16 15:10:13 CEST 2020
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 04 21:31:44 CEST 2021

121
pom.xml
View File

@ -61,10 +61,10 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven.minimumVersion>3.3.9</maven.minimumVersion>
<maven.minimumVersion>3.6.0</maven.minimumVersion>
<!-- Dependencies versions -->
<spigot.version>1.16.1-R0.1-SNAPSHOT</spigot.version>
<spigot.version>1.18-rc3-R0.1-SNAPSHOT</spigot.version>
<!-- Versioning properties -->
<project.outputName>AuthMe</project.outputName>
@ -76,7 +76,7 @@
<pluginDescription.name>${project.outputName}</pluginDescription.name>
<pluginDescription.version>${project.versionCode}</pluginDescription.version>
<pluginDescription.main>${project.groupId}.${project.artifactId}.${pluginDescription.name}</pluginDescription.main>
<pluginDescription.authors>Xephi, sgdc3, DNx5, timvisee, games647, ljacqu, Gnat008</pluginDescription.authors>
<pluginDescription.authors>sgdc3, ljacqu, games647, Hex3l, krusic22</pluginDescription.authors>
</properties>
<!-- Jenkins profile -->
@ -151,7 +151,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.1</version>
</plugin>
</plugins>
</pluginManagement>
@ -161,7 +161,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4.1</version>
<version>3.0.0</version>
<executions>
<execution>
<id>enforce-environment</id>
@ -205,7 +205,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.6</version>
<version>0.8.7</version>
<executions>
<execution>
<id>pre-unit-test</id>
@ -285,10 +285,43 @@
<version>3.2.4</version>
<executions>
<execution>
<id>shaded-jar</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<excludes>
<exclude>com.google.guava:guava</exclude>
<exclude>com.google.code.gson:gson</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
<execution>
<id>shaded-jar-legacy</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>${project.finalNameBase}-legacy</finalName>
<relocations>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>fr.xephi.authme.libs.com.google.common</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.thirdparty</pattern>
<shadedPattern>fr.xephi.authme.libs.com.google.thirdparty</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.gson</pattern>
<shadedPattern>fr.xephi.authme.libs.com.google.gson</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
<configuration>
@ -303,7 +336,7 @@
Relocate all lib we use in order to fix class loading errors if we use different versions
than already loaded libs
-->
<relocations>
<relocations combine.children="append">
<relocation>
<pattern>ch.jalu</pattern>
<shadedPattern>fr.xephi.authme.libs.ch.jalu</shadedPattern>
@ -361,6 +394,14 @@
<pattern>org.bstats</pattern>
<shadedPattern>fr.xephi.authme.libs.org.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>org.mariadb.jdbc</pattern>
<shadedPattern>fr.xephi.authme.libs.org.mariadb.jdbc</shadedPattern>
</relocation>
<relocation>
<pattern>com.mysql</pattern>
<shadedPattern>fr.xephi.authme.libs.com.mysql</shadedPattern>
</relocation>
</relocations>
<filters>
@ -372,9 +413,21 @@
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/*.MF</exclude>
<exclude>META-INF/DEPENDENCIES</exclude>
<exclude>META-INF/**/module-info.class</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer">
<addHeader>false</addHeader>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
</configuration>
</plugin>
<!-- Install the jars as artifacts into the local repository -->
@ -572,7 +625,7 @@
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.5</version>
<version>4.0.3</version>
<optional>true</optional>
<exclusions>
<exclusion>
@ -585,7 +638,7 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.30</version>
<version>1.7.32</version>
<optional>true</optional>
</dependency>
@ -597,11 +650,25 @@
<optional>true</optional>
</dependency>
<!-- MySQL connector, shaded into the legacy jar -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>2.7.4</version>
<optional>true</optional>
</dependency>
<!-- Argon2 implementation -->
<dependency>
<groupId>de.mkammerer</groupId>
<artifactId>argon2-jvm-nolibs</artifactId>
<version>2.7</version>
<version>2.11</version>
<optional>true</optional>
</dependency>
@ -634,19 +701,19 @@
</exclusion>
</exclusions>
</dependency>
<!-- Keep in sync with spigot 1.8.8 -->
<!-- Keep in sync with spigot 1.18 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>17.0</version>
<scope>provided</scope>
<version>31.0.1-jre</version>
<optional>true</optional>
</dependency>
<!-- Keep in sync with spigot 1.8.8 -->
<!-- Keep in sync with spigot 1.18 -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.4</version>
<scope>provided</scope>
<version>2.8.8</version>
<optional>true</optional>
</dependency>
<!-- Bukkit Libraries -->
@ -655,7 +722,7 @@
<dependency>
<groupId>ch.jalu</groupId>
<artifactId>configme</artifactId>
<version>1.2.0</version>
<version>1.3.0</version>
<optional>true</optional>
<exclusions>
<exclusion>
@ -669,15 +736,15 @@
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>1.7</version>
<version>2.2.1</version>
<optional>true</optional>
</dependency>
<!-- ProtocolLib -->
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-API</artifactId>
<version>4.4.0</version>
<artifactId>ProtocolLib</artifactId>
<version>4.7.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
@ -695,7 +762,7 @@
<dependency>
<groupId>net.luckperms</groupId>
<artifactId>api</artifactId>
<version>5.2</version>
<version>5.3</version>
<scope>provided</scope>
</dependency>
@ -753,7 +820,7 @@
<dependency>
<groupId>com.onarandombox.multiversecore</groupId>
<artifactId>Multiverse-Core</artifactId>
<version>4.2.2</version>
<version>4.3.1</version>
<type>jar</type>
<scope>provided</scope>
<exclusions>
@ -880,7 +947,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.18</version>
<version>42.3.1</version>
<optional>true</optional>
</dependency>
@ -890,7 +957,7 @@
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<version>4.13.1</version>
<version>4.13.2</version>
</dependency>
<dependency>
@ -904,7 +971,7 @@
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
<version>3.6.28</version>
<version>4.1.0</version>
<exclusions>
<exclusion>
<artifactId>hamcrest-core</artifactId>
@ -917,7 +984,7 @@
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>3.8.0</version>
<version>3.20.0</version>
<scope>test</scope>
</dependency>
@ -925,7 +992,7 @@
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.32.3.2</version>
<version>3.36.0.3</version>
<scope>test</scope>
</dependency>
<dependency>

View File

@ -70,6 +70,27 @@ abstract class AuthMeController {
}
return false;
}
/**
* Changes password for player.
*
* @param string $username the username
* @param string $password the password
* @return bool true whether or not password change was successful
*/
function changePassword($username, $password) {
$mysqli = $this->getAuthmeMySqli();
if ($mysqli !== null) {
$hash = $this->hash($password);
$stmt = $mysqli->prepare('UPDATE ' . self::AUTHME_TABLE . ' SET password=? '
. 'WHERE username=?');
$username_low = strtolower($username);
$stmt->bind_param('ss', $hash, $username_low);
return $stmt->execute();
}
return false;
}
/**
* Hashes the given password.

View File

@ -8,7 +8,9 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;
public class VersionCommand implements ExecutableCommand {
@ -22,21 +24,25 @@ public class VersionCommand implements ExecutableCommand {
sender.sendMessage(ChatColor.GOLD + "==========[ " + AuthMe.getPluginName() + " ABOUT ]==========");
sender.sendMessage(ChatColor.GOLD + "Version: " + ChatColor.WHITE + AuthMe.getPluginName()
+ " v" + AuthMe.getPluginVersion() + ChatColor.GRAY + " (build: " + AuthMe.getPluginBuildNumber() + ")");
sender.sendMessage(ChatColor.GOLD + "Developers:");
sender.sendMessage(ChatColor.GOLD + "Authors:");
Collection<Player> onlinePlayers = bukkitService.getOnlinePlayers();
printDeveloper(sender, "Alexandre Vanhecke", "xephi59", "Original Author", onlinePlayers);
printDeveloper(sender, "Lucas J.", "ljacqu", "Main Developer", onlinePlayers);
printDeveloper(sender, "Gnat008", "gnat008", "Developer", onlinePlayers);
printDeveloper(sender, "DNx5", "DNx5", "Developer", onlinePlayers);
printDeveloper(sender, "games647", "games647", "Developer", onlinePlayers);
printDeveloper(sender, "Tim Visee", "timvisee", "Developer", onlinePlayers);
printDeveloper(sender, "Gabriele C.", "sgdc3", "Project manager, Contributor", onlinePlayers);
printDeveloper(sender, "Lucas J.", "ljacqu", "Main Developer", onlinePlayers);
printDeveloper(sender, "games647", "games647", "Developer", onlinePlayers);
printDeveloper(sender, "Hex3l", "Hex3l", "Developer", onlinePlayers);
printDeveloper(sender, "krusic22", "krusic22", "Support", onlinePlayers);
sender.sendMessage(ChatColor.GOLD + "Retired authors:");
printDeveloper(sender, "Alexandre Vanhecke", "xephi59", "Original Author", onlinePlayers);
printDeveloper(sender, "Gnat008", "gnat008", "Developer, Retired", onlinePlayers);
printDeveloper(sender, "DNx5", "DNx5", "Developer, Retired", onlinePlayers);
printDeveloper(sender, "Tim Visee", "timvisee", "Developer, Retired", onlinePlayers);
sender.sendMessage(ChatColor.GOLD + "Website: " + ChatColor.WHITE
+ "http://dev.bukkit.org/bukkit-plugins/authme-reloaded/");
+ "https://github.com/AuthMe/AuthMeReloaded");
sender.sendMessage(ChatColor.GOLD + "License: " + ChatColor.WHITE + "GNU GPL v3.0"
+ ChatColor.GRAY + ChatColor.ITALIC + " (See LICENSE file)");
sender.sendMessage(ChatColor.GOLD + "Copyright: " + ChatColor.WHITE
+ "Copyright (c) AuthMe-Team 2019. Released under GPL v3 License.");
+ "Copyright (c) AuthMe-Team " + new SimpleDateFormat("yyyy").format(new Date())
+ ". Released under GPL v3 License.");
}
/**

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.data.limbo.UserGroup;
import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsManager;
@ -11,6 +12,8 @@ import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
import static java.util.stream.Collectors.toList;
/**
* Outputs the permission groups of a player.
*/
@ -37,8 +40,11 @@ class PermissionGroups implements DebugSection {
if (player == null) {
sender.sendMessage("Player " + name + " could not be found");
} else {
sender.sendMessage("Player " + name + " has permission groups: "
+ String.join(", ", permissionsManager.getGroups(player)));
List<String> groupNames = permissionsManager.getGroups(player).stream()
.map(UserGroup::getGroupName)
.collect(toList());
sender.sendMessage("Player " + name + " has permission groups: " + String.join(", ", groupNames));
sender.sendMessage("Primary group is: " + permissionsManager.getGroups(player));
}
}

View File

@ -0,0 +1,48 @@
package fr.xephi.authme.data;
import fr.xephi.authme.initialization.HasCleanup;
import fr.xephi.authme.util.expiring.ExpiringSet;
import javax.inject.Inject;
import java.util.concurrent.TimeUnit;
public class ProxySessionManager implements HasCleanup {
private final ExpiringSet<String> activeProxySessions;
@Inject
public ProxySessionManager() {
long countTimeout = 5;
activeProxySessions = new ExpiringSet<>(countTimeout, TimeUnit.SECONDS);
}
/**
* Saves the player in the set
* @param name the player's name
*/
private void setActiveSession(String name) {
activeProxySessions.add(name.toLowerCase());
}
/**
* Process a proxy session message from AuthMeBungee
* @param name the player to process
*/
public void processProxySessionMessage(String name) {
setActiveSession(name);
}
/**
* Returns if the player should be logged in or not
* @param name the name of the player to check
* @return true if player has to be logged in, false otherwise
*/
public boolean shouldResumeSession(String name) {
return activeProxySessions.contains(name);
}
@Override
public void performCleanup() {
activeProxySessions.removeExpiredEntries();
}
}

View File

@ -35,8 +35,8 @@ class AuthGroupHandler implements Reloadable {
@Inject
private Settings settings;
private String unregisteredGroup;
private String registeredGroup;
private UserGroup unregisteredGroup;
private UserGroup registeredGroup;
AuthGroupHandler() {
}
@ -53,7 +53,7 @@ class AuthGroupHandler implements Reloadable {
return;
}
Collection<String> previousGroups = limbo == null ? Collections.emptyList() : limbo.getGroups();
Collection<UserGroup> previousGroups = limbo == null ? Collections.emptyList() : limbo.getGroups();
switch (groupType) {
// Implementation note: some permission systems don't support players not being in any group,
@ -107,8 +107,8 @@ class AuthGroupHandler implements Reloadable {
@Override
@PostConstruct
public void reload() {
unregisteredGroup = settings.getProperty(PluginSettings.UNREGISTERED_GROUP);
registeredGroup = settings.getProperty(PluginSettings.REGISTERED_GROUP);
unregisteredGroup = new UserGroup(settings.getProperty(PluginSettings.UNREGISTERED_GROUP));
registeredGroup = new UserGroup(settings.getProperty(PluginSettings.REGISTERED_GROUP));
}
}

View File

@ -4,6 +4,7 @@ import fr.xephi.authme.task.MessageTask;
import org.bukkit.Location;
import org.bukkit.scheduler.BukkitTask;
import java.util.ArrayList;
import java.util.Collection;
/**
@ -17,7 +18,7 @@ public class LimboPlayer {
private final boolean canFly;
private final boolean operator;
private final Collection<String> groups;
private final Collection<UserGroup> groups;
private final Location loc;
private final float walkSpeed;
private final float flySpeed;
@ -25,11 +26,11 @@ public class LimboPlayer {
private MessageTask messageTask = null;
private LimboPlayerState state = LimboPlayerState.PASSWORD_REQUIRED;
public LimboPlayer(Location loc, boolean operator, Collection<String> groups, boolean fly, float walkSpeed,
public LimboPlayer(Location loc, boolean operator, Collection<UserGroup> groups, boolean fly, float walkSpeed,
float flySpeed) {
this.loc = loc;
this.operator = operator;
this.groups = groups;
this.groups = new ArrayList<>(groups); // prevent bug #2413
this.canFly = fly;
this.walkSpeed = walkSpeed;
this.flySpeed = flySpeed;
@ -58,7 +59,7 @@ public class LimboPlayer {
*
* @return The permissions groups the player belongs to
*/
public Collection<String> getGroups() {
public Collection<UserGroup> getGroups() {
return groups;
}

View File

@ -12,8 +12,10 @@ import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static fr.xephi.authme.util.Utils.isCollectionEmpty;
import static java.util.stream.Collectors.toList;
/**
* Helper class for the LimboService.
@ -31,9 +33,9 @@ class LimboServiceHelper {
/**
* Creates a LimboPlayer with the given player's details.
*
* @param player the player to process
* @param player the player to process
* @param isRegistered whether the player is registered
* @param location the player location
* @param location the player location
* @return limbo player with the player's data
*/
LimboPlayer createLimboPlayer(Player player, boolean isRegistered, Location location) {
@ -42,10 +44,14 @@ class LimboServiceHelper {
boolean flyEnabled = player.getAllowFlight();
float walkSpeed = player.getWalkSpeed();
float flySpeed = player.getFlySpeed();
Collection<String> playerGroups = permissionsManager.hasGroupSupport()
Collection<UserGroup> playerGroups = permissionsManager.hasGroupSupport()
? permissionsManager.getGroups(player) : Collections.emptyList();
logger.debug("Player `{0}` has groups `{1}`", player.getName(), String.join(", ", playerGroups));
List<String> groupNames = playerGroups.stream()
.map(UserGroup::getGroupName)
.collect(toList());
logger.debug("Player `{0}` has groups `{1}`", player.getName(), String.join(", ", groupNames));
return new LimboPlayer(location, isOperator, playerGroups, flyEnabled, walkSpeed, flySpeed);
}
@ -91,7 +97,7 @@ class LimboServiceHelper {
boolean canFly = newLimbo.isCanFly() || oldLimbo.isCanFly();
float flySpeed = Math.max(newLimbo.getFlySpeed(), oldLimbo.getFlySpeed());
float walkSpeed = Math.max(newLimbo.getWalkSpeed(), oldLimbo.getWalkSpeed());
Collection<String> groups = getLimboGroups(oldLimbo.getGroups(), newLimbo.getGroups());
Collection<UserGroup> groups = getLimboGroups(oldLimbo.getGroups(), newLimbo.getGroups());
Location location = firstNotNull(oldLimbo.getLocation(), newLimbo.getLocation());
return new LimboPlayer(location, isOperator, groups, canFly, walkSpeed, flySpeed);
@ -101,7 +107,8 @@ class LimboServiceHelper {
return first == null ? second : first;
}
private Collection<String> getLimboGroups(Collection<String> oldLimboGroups, Collection<String> newLimboGroups) {
private Collection<UserGroup> getLimboGroups(Collection<UserGroup> oldLimboGroups,
Collection<UserGroup> newLimboGroups) {
logger.debug("Limbo merge: new and old groups are `{0}` and `{1}`", newLimboGroups, oldLimboGroups);
return isCollectionEmpty(oldLimboGroups) ? newLimboGroups : oldLimboGroups;
}

View File

@ -0,0 +1,45 @@
package fr.xephi.authme.data.limbo;
import java.util.Map;
import java.util.Objects;
public class UserGroup {
private String groupName;
private Map<String, String> contextMap;
public UserGroup(String groupName) {
this.groupName = groupName;
}
public UserGroup(String groupName, Map<String, String> contextMap) {
this.groupName = groupName;
this.contextMap = contextMap;
}
public String getGroupName() {
return groupName;
}
public Map<String, String> getContextMap() {
return contextMap;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
UserGroup userGroup = (UserGroup) o;
return Objects.equals(groupName, userGroup.groupName)
&& Objects.equals(contextMap, userGroup.contextMap);
}
@Override
public int hashCode() {
return Objects.hash(groupName, contextMap);
}
}

View File

@ -1,11 +1,14 @@
package fr.xephi.authme.data.limbo.persistence;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import fr.xephi.authme.data.limbo.LimboPlayer;
import fr.xephi.authme.data.limbo.UserGroup;
import fr.xephi.authme.service.BukkitService;
import org.bukkit.Location;
import org.bukkit.World;
@ -15,6 +18,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static fr.xephi.authme.data.limbo.persistence.LimboPlayerSerializer.CAN_FLY;
@ -37,6 +41,8 @@ import static java.util.Optional.ofNullable;
class LimboPlayerDeserializer implements JsonDeserializer<LimboPlayer> {
private static final String GROUP_LEGACY = "group";
private static final String CONTEXT_MAP = "contextMap";
private static final String GROUP_NAME = "groupName";
private BukkitService bukkitService;
@ -54,7 +60,7 @@ class LimboPlayerDeserializer implements JsonDeserializer<LimboPlayer> {
Location loc = deserializeLocation(jsonObject);
boolean operator = getBoolean(jsonObject, IS_OP);
Collection<String> groups = getLimboGroups(jsonObject);
Collection<UserGroup> groups = getLimboGroups(jsonObject);
boolean canFly = getBoolean(jsonObject, CAN_FLY);
float walkSpeed = getFloat(jsonObject, WALK_SPEED, LimboPlayer.DEFAULT_WALK_SPEED);
float flySpeed = getFloat(jsonObject, FLY_SPEED, LimboPlayer.DEFAULT_FLY_SPEED);
@ -84,16 +90,35 @@ class LimboPlayerDeserializer implements JsonDeserializer<LimboPlayer> {
return element != null ? element.getAsString() : "";
}
private static List<String> getLimboGroups(JsonObject jsonObject) {
/**
* @param jsonObject LimboPlayer represented as JSON
* @return The list of UserGroups create from JSON
*/
private static List<UserGroup> getLimboGroups(JsonObject jsonObject) {
JsonElement element = jsonObject.get(GROUPS);
if (element == null) {
String legacyGroup = ofNullable(jsonObject.get(GROUP_LEGACY)).map(JsonElement::getAsString).orElse(null);
return legacyGroup == null ? Collections.emptyList() : Collections.singletonList(legacyGroup);
return legacyGroup == null ? Collections.emptyList() :
Collections.singletonList(new UserGroup(legacyGroup, null));
}
List<String> result = new ArrayList<>();
List<UserGroup> result = new ArrayList<>();
JsonArray jsonArray = element.getAsJsonArray();
for (JsonElement arrayElement : jsonArray) {
result.add(arrayElement.getAsString());
if (!arrayElement.isJsonObject()) {
result.add(new UserGroup(arrayElement.getAsString(), null));
} else {
JsonObject jsonGroup = arrayElement.getAsJsonObject();
Map<String, String> contextMap = null;
if (jsonGroup.has(CONTEXT_MAP)) {
JsonElement contextMapJson = jsonGroup.get("contextMap");
Type type = new TypeToken<Map<String, String>>() {
}.getType();
contextMap = new Gson().fromJson(contextMapJson.getAsString(), type);
}
String groupName = jsonGroup.get(GROUP_NAME).getAsString();
result.add(new UserGroup(groupName, contextMap));
}
}
return result;
}
@ -122,7 +147,6 @@ class LimboPlayerDeserializer implements JsonDeserializer<LimboPlayer> {
* @param numberFunction the function to get the number from the element
* @param defaultValue the value to return if the element is null or the number cannot be retrieved
* @param <N> the number type
*
* @return the number from the given JSON element, or the default value
*/
private static <N extends Number> N getNumberFromElement(JsonElement jsonElement,

View File

@ -1,6 +1,7 @@
package fr.xephi.authme.data.limbo.persistence;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
@ -9,6 +10,8 @@ import fr.xephi.authme.data.limbo.LimboPlayer;
import org.bukkit.Location;
import java.lang.reflect.Type;
import java.util.List;
import java.util.stream.Collectors;
/**
* Converts a LimboPlayer to a JsonElement.
@ -45,7 +48,19 @@ class LimboPlayerSerializer implements JsonSerializer<LimboPlayer> {
JsonObject obj = new JsonObject();
obj.add(LOCATION, locationObject);
obj.add(GROUPS, GSON.toJsonTree(limboPlayer.getGroups()).getAsJsonArray());
List<JsonObject> groups = limboPlayer.getGroups().stream().map(g -> {
JsonObject jsonGroup = new JsonObject();
jsonGroup.addProperty("groupName", g.getGroupName());
if (g.getContextMap() != null) {
jsonGroup.addProperty("contextMap", GSON.toJson(g.getContextMap()));
}
return jsonGroup;
}).collect(Collectors.toList());
JsonArray jsonGroups = new JsonArray();
groups.forEach(jsonGroups::add);
obj.add(GROUPS, jsonGroups);
obj.addProperty(IS_OP, limboPlayer.isOperator());
obj.addProperty(CAN_FLY, limboPlayer.isCanFly());

View File

@ -43,6 +43,7 @@ public class MySQL extends AbstractSqlDataSource {
private String port;
private String username;
private String password;
private String className;
private String database;
private String tableName;
private int poolSize;
@ -99,6 +100,13 @@ public class MySQL extends AbstractSqlDataSource {
this.port = settings.getProperty(DatabaseSettings.MYSQL_PORT);
this.username = settings.getProperty(DatabaseSettings.MYSQL_USERNAME);
this.password = settings.getProperty(DatabaseSettings.MYSQL_PASSWORD);
this.className = settings.getProperty(DatabaseSettings.MYSQL_DRIVER_CLASS_NAME);
try {
Class.forName(this.className);
} catch (ClassNotFoundException e) {
this.className = DatabaseSettings.MYSQL_DRIVER_CLASS_NAME.getDefaultValue();
logger.info("Driver class '" + this.className + "' not found! Falling back to the built-in MySQL driver (" + this.className + ")");
}
this.database = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
this.columnOthers = settings.getProperty(HooksSettings.MYSQL_OTHER_USERNAME_COLS);
@ -120,7 +128,7 @@ public class MySQL extends AbstractSqlDataSource {
// Pool Settings
ds.setMaximumPoolSize(poolSize);
ds.setMaxLifetime(maxLifetime * 1000);
ds.setMaxLifetime(maxLifetime * 1000L);
// Database URL
ds.setJdbcUrl("jdbc:mysql://" + this.host + ":" + this.port + "/" + this.database);
@ -128,6 +136,9 @@ public class MySQL extends AbstractSqlDataSource {
// Auth
ds.setUsername(this.username);
ds.setPassword(this.password);
// Driver
ds.setDriverClassName(this.className);
// Request mysql over SSL
ds.addDataSourceProperty("useSSL", String.valueOf(useSsl));

View File

@ -88,7 +88,7 @@ public class SQLite extends AbstractSqlDataSource {
}
logger.debug("SQLite driver loaded");
this.con = DriverManager.getConnection("jdbc:sqlite:plugins/AuthMe/" + database + ".db");
this.con = DriverManager.getConnection("jdbc:sqlite:" + this.dataFolder + File.separator + database + ".db");
this.columnsHandler = AuthMeColumnsHandler.createForSqlite(con, settings);
}

View File

@ -59,6 +59,8 @@ public class LoginSecurityConverter implements Converter {
try (Connection connection = createConnectionOrInformSender(sender)) {
if (connection != null) {
performConversion(sender, connection);
logger.info("LoginSecurity conversion completed! Please remember to set \"legacyHashes: ['BCRYPT']\" "
+ "in your configuration file!");
}
} catch (SQLException e) {
sender.sendMessage("Failed to convert from SQLite. Please see the log for more info");

View File

@ -15,6 +15,7 @@ import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.settings.properties.PluginSettings;
import org.apache.logging.log4j.LogManager;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
@ -54,9 +55,9 @@ public class OnStartupTasks {
public static void sendMetrics(AuthMe plugin, Settings settings) {
final Metrics metrics = new Metrics(plugin, 164);
metrics.addCustomChart(new Metrics.SimplePie("messages_language",
metrics.addCustomChart(new SimplePie("messages_language",
() -> settings.getProperty(PluginSettings.MESSAGES_LANGUAGE)));
metrics.addCustomChart(new Metrics.SimplePie("database_backend",
metrics.addCustomChart(new SimplePie("database_backend",
() -> settings.getProperty(DatabaseSettings.BACKEND).toString()));
}

View File

@ -190,7 +190,7 @@ public class PlayerListener implements Listener {
teleportationService.teleportNewPlayerToFirstSpawn(player);
}
@EventHandler(priority = EventPriority.HIGHEST)
@EventHandler(priority = EventPriority.HIGH) // HIGH as EssentialsX listens at HIGHEST
public void onJoinMessage(PlayerJoinEvent event) {
final Player player = event.getPlayer();

View File

@ -2,6 +2,7 @@ package fr.xephi.authme.permission;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.limbo.UserGroup;
import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.permission.handlers.LuckPermsHandler;
@ -285,7 +286,7 @@ public class PermissionsManager implements Reloadable {
*
* @return Permission groups, or an empty collection if this feature is not supported.
*/
public Collection<String> getGroups(OfflinePlayer player) {
public Collection<UserGroup> getGroups(OfflinePlayer player) {
return isEnabled() ? handler.getGroups(player) : Collections.emptyList();
}
@ -296,7 +297,7 @@ public class PermissionsManager implements Reloadable {
*
* @return The name of the primary permission group. Or null.
*/
public String getPrimaryGroup(OfflinePlayer player) {
public UserGroup getPrimaryGroup(OfflinePlayer player) {
return isEnabled() ? handler.getPrimaryGroup(player) : null;
}
@ -309,7 +310,7 @@ public class PermissionsManager implements Reloadable {
* @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.
*/
public boolean isInGroup(OfflinePlayer player, String groupName) {
public boolean isInGroup(OfflinePlayer player, UserGroup groupName) {
return isEnabled() && handler.isInGroup(player, groupName);
}
@ -322,8 +323,8 @@ public class PermissionsManager implements Reloadable {
* @return True if succeed, false otherwise.
* 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)) {
public boolean addGroup(OfflinePlayer player, UserGroup groupName) {
if (!isEnabled() || StringUtils.isEmpty(groupName.getGroupName())) {
return false;
}
return handler.addToGroup(player, groupName);
@ -338,7 +339,7 @@ public class PermissionsManager implements Reloadable {
* @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.
*/
public boolean addGroups(OfflinePlayer player, Collection<String> groupNames) {
public boolean addGroups(OfflinePlayer player, Collection<UserGroup> groupNames) {
// If no permissions system is used, return false
if (!isEnabled()) {
return false;
@ -346,9 +347,9 @@ public class PermissionsManager implements Reloadable {
// Add each group to the user
boolean result = false;
for (String groupName : groupNames) {
if (!groupName.isEmpty()) {
result |= handler.addToGroup(player, groupName);
for (UserGroup group : groupNames) {
if (!group.getGroupName().isEmpty()) {
result |= handler.addToGroup(player, group);
}
}
@ -360,13 +361,13 @@ public class PermissionsManager implements Reloadable {
* Remove the permission group of a player, if supported.
*
* @param player The player
* @param groupName The name of the group.
* @param group 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.
*/
public boolean removeGroup(OfflinePlayer player, String groupName) {
return isEnabled() && handler.removeFromGroup(player, groupName);
public boolean removeGroup(OfflinePlayer player, UserGroup group) {
return isEnabled() && handler.removeFromGroup(player, group);
}
/**
@ -378,7 +379,7 @@ public class PermissionsManager implements Reloadable {
* @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.
*/
public boolean removeGroups(OfflinePlayer player, Collection<String> groupNames) {
public boolean removeGroups(OfflinePlayer player, Collection<UserGroup> groupNames) {
// If no permissions system is used, return false
if (!isEnabled()) {
return false;
@ -386,9 +387,9 @@ public class PermissionsManager implements Reloadable {
// Add each group to the user
boolean result = false;
for (String groupName : groupNames) {
if (!groupName.isEmpty()) {
result |= handler.removeFromGroup(player, groupName);
for (UserGroup group : groupNames) {
if (!group.getGroupName().isEmpty()) {
result |= handler.removeFromGroup(player, group);
}
}
@ -401,13 +402,13 @@ public class PermissionsManager implements Reloadable {
* This clears the current groups of the player.
*
* @param player The player
* @param groupName The name of the group.
* @param group 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.
*/
public boolean setGroup(OfflinePlayer player, String groupName) {
return isEnabled() && handler.setGroup(player, groupName);
public boolean setGroup(OfflinePlayer player, UserGroup group) {
return isEnabled() && handler.setGroup(player, group);
}
/**
@ -427,10 +428,10 @@ public class PermissionsManager implements Reloadable {
}
// Get a list of current groups
Collection<String> groupNames = getGroups(player);
Collection<UserGroup> groups = getGroups(player);
// Remove each group
return removeGroups(player, groupNames);
return removeGroups(player, groups);
}
/**

View File

@ -0,0 +1,23 @@
package fr.xephi.authme.permission.handlers;
import net.luckperms.api.context.ImmutableContextSet;
import net.luckperms.api.model.group.Group;
public class LuckPermGroup {
private Group group;
private ImmutableContextSet contexts;
public LuckPermGroup(Group group, ImmutableContextSet contexts) {
this.group = group;
this.contexts = contexts;
}
public Group getGroup() {
return group;
}
public ImmutableContextSet getContexts() {
return contexts;
}
}

View File

@ -1,12 +1,14 @@
package fr.xephi.authme.permission.handlers;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.limbo.UserGroup;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsSystemType;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.cacheddata.CachedPermissionData;
import net.luckperms.api.context.ContextSetFactory;
import net.luckperms.api.model.data.DataMutateResult;
import net.luckperms.api.model.group.Group;
import net.luckperms.api.model.user.User;
@ -15,6 +17,7 @@ import net.luckperms.api.node.types.InheritanceNode;
import net.luckperms.api.query.QueryMode;
import net.luckperms.api.query.QueryOptions;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
@ -45,8 +48,8 @@ public class LuckPermsHandler implements PermissionHandler {
}
@Override
public boolean addToGroup(OfflinePlayer player, String group) {
Group newGroup = luckPerms.getGroupManager().getGroup(group);
public boolean addToGroup(OfflinePlayer player, UserGroup group) {
Group newGroup = luckPerms.getGroupManager().getGroup(group.getGroupName());
if (newGroup == null) {
return false;
}
@ -60,7 +63,8 @@ public class LuckPermsHandler implements PermissionHandler {
return false;
}
InheritanceNode node = InheritanceNode.builder(group).build();
InheritanceNode node = buildGroupNode(group);
DataMutateResult result = user.data().add(node);
if (result == DataMutateResult.FAIL) {
return false;
@ -85,12 +89,12 @@ public class LuckPermsHandler implements PermissionHandler {
}
CachedPermissionData permissionData = user.getCachedData()
.getPermissionData(QueryOptions.builder(QueryMode.NON_CONTEXTUAL).build());
.getPermissionData(QueryOptions.builder(QueryMode.CONTEXTUAL).build());
return permissionData.checkPermission(node.getNode()).asBoolean();
}
@Override
public boolean isInGroup(OfflinePlayer player, String group) {
public boolean isInGroup(OfflinePlayer player, UserGroup group) {
String playerName = player.getName();
if (playerName == null) {
return false;
@ -102,12 +106,12 @@ public class LuckPermsHandler implements PermissionHandler {
return false;
}
InheritanceNode inheritanceNode = InheritanceNode.builder(group).build();
InheritanceNode inheritanceNode = InheritanceNode.builder(group.getGroupName()).build();
return user.data().contains(inheritanceNode, NodeEqualityPredicate.EXACT).asBoolean();
}
@Override
public boolean removeFromGroup(OfflinePlayer player, String group) {
public boolean removeFromGroup(OfflinePlayer player, UserGroup group) {
String playerName = player.getName();
if (playerName == null) {
return false;
@ -119,7 +123,7 @@ public class LuckPermsHandler implements PermissionHandler {
return false;
}
InheritanceNode groupNode = InheritanceNode.builder(group).build();
InheritanceNode groupNode = InheritanceNode.builder(group.getGroupName()).build();
boolean result = user.data().remove(groupNode) != DataMutateResult.FAIL;
luckPerms.getUserManager().saveUser(user);
@ -127,7 +131,7 @@ public class LuckPermsHandler implements PermissionHandler {
}
@Override
public boolean setGroup(OfflinePlayer player, String group) {
public boolean setGroup(OfflinePlayer player, UserGroup group) {
String playerName = player.getName();
if (playerName == null) {
return false;
@ -138,7 +142,9 @@ public class LuckPermsHandler implements PermissionHandler {
+ player.getName() + " but it isn't loaded!");
return false;
}
InheritanceNode groupNode = InheritanceNode.builder(group).build();
InheritanceNode groupNode = buildGroupNode(group);
DataMutateResult result = user.data().add(groupNode);
if (result == DataMutateResult.FAIL) {
return false;
@ -156,7 +162,7 @@ public class LuckPermsHandler implements PermissionHandler {
}
@Override
public List<String> getGroups(OfflinePlayer player) {
public List<UserGroup> getGroups(OfflinePlayer player) {
String playerName = player.getName();
if (playerName == null) {
return Collections.emptyList();
@ -171,17 +177,16 @@ public class LuckPermsHandler implements PermissionHandler {
return user.getDistinctNodes().stream()
.filter(node -> node instanceof InheritanceNode)
.map(node -> (InheritanceNode) node)
.map(node -> luckPerms.getGroupManager().getGroup(node.getGroupName()))
.filter(Objects::nonNull)
.sorted((o1, o2) -> {
if (o1.getName().equals(user.getPrimaryGroup()) || o2.getName().equals(user.getPrimaryGroup())) {
return o1.getName().equals(user.getPrimaryGroup()) ? 1 : -1;
.map(node -> {
Group group = luckPerms.getGroupManager().getGroup(node.getGroupName());
if (group == null) {
return null;
}
int i = Integer.compare(o2.getWeight().orElse(0), o1.getWeight().orElse(0));
return i != 0 ? i : o1.getName().compareToIgnoreCase(o2.getName());
return new LuckPermGroup(group, node.getContexts());
})
.map(Group::getName)
.filter(Objects::nonNull)
.sorted((o1, o2) -> sortGroups(user, o1, o2))
.map(g -> new UserGroup(g.getGroup().getName(), g.getContexts().toFlattenedMap()))
.collect(Collectors.toList());
}
@ -199,4 +204,24 @@ public class LuckPermsHandler implements PermissionHandler {
}
}
@NotNull
private InheritanceNode buildGroupNode(UserGroup group) {
ContextSetFactory contextSetFactory = luckPerms.getContextManager().getContextSetFactory();
InheritanceNode.Builder builder = InheritanceNode.builder(group.getGroupName());
if (group.getContextMap() != null) {
group.getContextMap().forEach((k, v) -> builder.withContext((contextSetFactory.immutableOf(k, v))));
}
return builder.build();
}
private int sortGroups(User user, LuckPermGroup o1, LuckPermGroup o2) {
Group group1 = o1.getGroup();
Group group2 = o2.getGroup();
if (group1.getName().equals(user.getPrimaryGroup()) || group2.getName().equals(user.getPrimaryGroup())) {
return group1.getName().equals(user.getPrimaryGroup()) ? 1 : -1;
}
int i = Integer.compare(group2.getWeight().orElse(0), group1.getWeight().orElse(0));
return i != 0 ? i : group1.getName().compareToIgnoreCase(group2.getName());
}
}

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.permission.handlers;
import fr.xephi.authme.data.limbo.UserGroup;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsSystemType;
import fr.xephi.authme.util.Utils;
@ -19,7 +20,7 @@ public interface PermissionHandler {
* @return True if succeed, false otherwise.
* False is also returned if this feature isn't supported for the current permissions system.
*/
boolean addToGroup(OfflinePlayer player, String group);
boolean addToGroup(OfflinePlayer player, UserGroup group);
/**
* Check whether the current permissions system has group support.
@ -49,7 +50,7 @@ public interface PermissionHandler {
* @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.
*/
default boolean isInGroup(OfflinePlayer player, String group) {
default boolean isInGroup(OfflinePlayer player, UserGroup group) {
return getGroups(player).contains(group);
}
@ -62,7 +63,7 @@ public interface PermissionHandler {
* @return True if succeed, false otherwise.
* False is also returned if this feature isn't supported for the current permissions system.
*/
boolean removeFromGroup(OfflinePlayer player, String group);
boolean removeFromGroup(OfflinePlayer player, UserGroup group);
/**
* Set the permission group of a player, if supported.
@ -74,7 +75,7 @@ public interface PermissionHandler {
* @return True if succeed, false otherwise.
* False is also returned if this feature isn't supported for the current permissions system.
*/
boolean setGroup(OfflinePlayer player, String group);
boolean setGroup(OfflinePlayer player, UserGroup group);
/**
* Get the permission groups of a player, if available.
@ -83,7 +84,7 @@ public interface PermissionHandler {
*
* @return Permission groups, or an empty list if this feature is not supported.
*/
Collection<String> getGroups(OfflinePlayer player);
Collection<UserGroup> getGroups(OfflinePlayer player);
/**
* Get the primary group of a player, if available.
@ -92,8 +93,8 @@ public interface PermissionHandler {
*
* @return The name of the primary permission group. Or null.
*/
default String getPrimaryGroup(OfflinePlayer player) {
Collection<String> groups = getGroups(player);
default UserGroup getPrimaryGroup(OfflinePlayer player) {
Collection<UserGroup> groups = getGroups(player);
if (Utils.isCollectionEmpty(groups)) {
return null;
}

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.permission.handlers;
import fr.xephi.authme.data.limbo.UserGroup;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsSystemType;
import org.bukkit.OfflinePlayer;
@ -10,6 +11,8 @@ import ru.tehkode.permissions.bukkit.PermissionsEx;
import java.util.ArrayList;
import java.util.List;
import static java.util.stream.Collectors.toList;
/**
* Handler for PermissionsEx.
*
@ -28,13 +31,13 @@ public class PermissionsExHandler implements PermissionHandler {
}
@Override
public boolean addToGroup(OfflinePlayer player, String group) {
public boolean addToGroup(OfflinePlayer player, UserGroup group) {
if (!PermissionsEx.getPermissionManager().getGroupNames().contains(group)) {
return false;
}
PermissionUser user = PermissionsEx.getUser(player.getName());
user.addGroup(group);
user.addGroup(group.getGroupName());
return true;
}
@ -50,22 +53,22 @@ public class PermissionsExHandler implements PermissionHandler {
}
@Override
public boolean isInGroup(OfflinePlayer player, String group) {
public boolean isInGroup(OfflinePlayer player, UserGroup group) {
PermissionUser user = permissionManager.getUser(player.getName());
return user.inGroup(group);
return user.inGroup(group.getGroupName());
}
@Override
public boolean removeFromGroup(OfflinePlayer player, String group) {
public boolean removeFromGroup(OfflinePlayer player, UserGroup group) {
PermissionUser user = permissionManager.getUser(player.getName());
user.removeGroup(group);
user.removeGroup(group.getGroupName());
return true;
}
@Override
public boolean setGroup(OfflinePlayer player, String group) {
public boolean setGroup(OfflinePlayer player, UserGroup group) {
List<String> groups = new ArrayList<>();
groups.add(group);
groups.add(group.getGroupName());
PermissionUser user = permissionManager.getUser(player.getName());
user.setParentsIdentifier(groups);
@ -73,9 +76,11 @@ public class PermissionsExHandler implements PermissionHandler {
}
@Override
public List<String> getGroups(OfflinePlayer player) {
public List<UserGroup> getGroups(OfflinePlayer player) {
PermissionUser user = permissionManager.getUser(player.getName());
return user.getParentIdentifiers(null);
return user.getParentIdentifiers(null).stream()
.map(i -> new UserGroup(i, null))
.collect(toList());
}
@Override

View File

@ -1,6 +1,7 @@
package fr.xephi.authme.permission.handlers;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.data.limbo.UserGroup;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsSystemType;
import net.milkbowl.vault.permission.Permission;
@ -12,6 +13,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static java.util.stream.Collectors.toList;
/**
* Handler for permissions via Vault.
*
@ -51,8 +54,8 @@ public class VaultHandler implements PermissionHandler {
}
@Override
public boolean addToGroup(OfflinePlayer player, String group) {
return vaultProvider.playerAddGroup(null, player, group);
public boolean addToGroup(OfflinePlayer player, UserGroup group) {
return vaultProvider.playerAddGroup(null, player, group.getGroupName());
}
@Override
@ -66,33 +69,33 @@ public class VaultHandler implements PermissionHandler {
}
@Override
public boolean isInGroup(OfflinePlayer player, String group) {
return vaultProvider.playerInGroup(null, player, group);
public boolean isInGroup(OfflinePlayer player, UserGroup group) {
return vaultProvider.playerInGroup(null, player, group.getGroupName());
}
@Override
public boolean removeFromGroup(OfflinePlayer player, String group) {
return vaultProvider.playerRemoveGroup(null, player, group);
public boolean removeFromGroup(OfflinePlayer player, UserGroup group) {
return vaultProvider.playerRemoveGroup(null, player, group.getGroupName());
}
@Override
public boolean setGroup(OfflinePlayer player, String group) {
for (String groupName : getGroups(player)) {
removeFromGroup(player, groupName);
public boolean setGroup(OfflinePlayer player, UserGroup group) {
for (UserGroup g : getGroups(player)) {
removeFromGroup(player, g);
}
return vaultProvider.playerAddGroup(null, player, group);
return vaultProvider.playerAddGroup(null, player, group.getGroupName());
}
@Override
public List<String> getGroups(OfflinePlayer player) {
public List<UserGroup> getGroups(OfflinePlayer player) {
String[] groups = vaultProvider.getPlayerGroups(null, player);
return groups == null ? Collections.emptyList() : Arrays.asList(groups);
return groups == null ? Collections.emptyList() : Arrays.stream(groups).map(UserGroup::new).collect(toList());
}
@Override
public String getPrimaryGroup(OfflinePlayer player) {
return vaultProvider.getPrimaryGroup(null, player);
public UserGroup getPrimaryGroup(OfflinePlayer player) {
return new UserGroup(vaultProvider.getPrimaryGroup(null, player));
}
@Override

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.permission.handlers;
import fr.xephi.authme.data.limbo.UserGroup;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsSystemType;
import org.bukkit.Bukkit;
@ -9,6 +10,8 @@ import org.tyrannyofheaven.bukkit.zPermissions.ZPermissionsService;
import java.util.Collection;
import java.util.Map;
import static java.util.stream.Collectors.toList;
/**
* Handler for zPermissions.
*
@ -29,9 +32,9 @@ public class ZPermissionsHandler implements PermissionHandler {
}
@Override
public boolean addToGroup(OfflinePlayer player, String group) {
public boolean addToGroup(OfflinePlayer player, UserGroup group) {
return Bukkit.dispatchCommand(Bukkit.getConsoleSender(),
"permissions player " + player.getName() + " addgroup " + group);
"permissions player " + player.getName() + " addgroup " + group.getGroupName());
}
@Override
@ -46,25 +49,27 @@ public class ZPermissionsHandler implements PermissionHandler {
}
@Override
public boolean removeFromGroup(OfflinePlayer player, String group) {
public boolean removeFromGroup(OfflinePlayer player, UserGroup group) {
return Bukkit.dispatchCommand(Bukkit.getConsoleSender(),
"permissions player " + player.getName() + " removegroup " + group);
"permissions player " + player.getName() + " removegroup " + group.getGroupName());
}
@Override
public boolean setGroup(OfflinePlayer player, String group) {
public boolean setGroup(OfflinePlayer player, UserGroup group) {
return Bukkit.dispatchCommand(Bukkit.getConsoleSender(),
"permissions player " + player.getName() + " setgroup " + group);
"permissions player " + player.getName() + " setgroup " + group.getGroupName());
}
@Override
public Collection<String> getGroups(OfflinePlayer player) {
return zPermissionsService.getPlayerGroups(player.getName());
public Collection<UserGroup> getGroups(OfflinePlayer player) {
return zPermissionsService.getPlayerGroups(player.getName()).stream()
.map(UserGroup::new)
.collect(toList());
}
@Override
public String getPrimaryGroup(OfflinePlayer player) {
return zPermissionsService.getPlayerPrimaryGroup(player.getName());
public UserGroup getPrimaryGroup(OfflinePlayer player) {
return new UserGroup(zPermissionsService.getPlayerPrimaryGroup(player.getName()));
}
@Override

View File

@ -56,6 +56,10 @@ public class Management {
runTask(player, () -> asynchronousLogin.forceLogin(player));
}
public void forceLogin(Player player, boolean quiet) {
runTask(() -> asynchronousLogin.forceLogin(player, quiet));
}
public void performLogout(Player player) {
runTask(player, () -> asynchronousLogout.logout(player));
}

View File

@ -1,6 +1,7 @@
package fr.xephi.authme.process.join;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.ProxySessionManager;
import fr.xephi.authme.data.limbo.LimboService;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.events.ProtectInventoryEvent;
@ -15,6 +16,8 @@ import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.PluginHookService;
import fr.xephi.authme.service.SessionService;
import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.service.bungeecord.BungeeSender;
import fr.xephi.authme.service.bungeecord.MessageType;
import fr.xephi.authme.settings.WelcomeMessageConfiguration;
import fr.xephi.authme.settings.commandconfig.CommandManager;
import fr.xephi.authme.settings.properties.HooksSettings;
@ -76,6 +79,12 @@ public class AsynchronousJoin implements AsynchronousProcess {
@Inject
private SessionService sessionService;
@Inject
private BungeeSender bungeeSender;
@Inject
private ProxySessionManager proxySessionManager;
AsynchronousJoin() {
}
@ -132,6 +141,15 @@ public class AsynchronousJoin implements AsynchronousProcess {
() -> commandManager.runCommandsOnSessionLogin(player));
asyncUserScheduler.runTask(name, () -> asynchronousLogin.forceLogin(player));
return;
} else if (proxySessionManager.shouldResumeSession(name)) {
service.send(player, MessageKey.SESSION_RECONNECTION);
// Run commands
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(
() -> commandManager.runCommandsOnSessionLogin(player));
bukkitService.runTaskOptionallyAsync(() -> asynchronousLogin.forceLogin(player));
logger.info("The user " + player.getName() + " has been automatically logged in, "
+ "as present in autologin queue.");
return;
}
} else if (!service.getProperty(RegistrationSettings.FORCE)) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {
@ -139,6 +157,7 @@ public class AsynchronousJoin implements AsynchronousProcess {
});
// Skip if registration is optional
bungeeSender.sendAuthMeBungeecordMessage(MessageType.LOGIN, name);
return;
}

View File

@ -118,6 +118,19 @@ public class AsynchronousLogin implements AsynchronousProcess {
}
}
/**
* Logs a player in without requiring a password.
*
* @param player the player to log in
* @param quiet if true no messages will be sent
*/
public void forceLogin(Player player, boolean quiet) {
PlayerAuth auth = getPlayerAuth(player, quiet);
if (auth != null) {
performLogin(player, auth);
}
}
/**
* Checks the precondition for authentication (like user known) and returns
* the player's {@link PlayerAuth} object.
@ -127,15 +140,32 @@ public class AsynchronousLogin implements AsynchronousProcess {
* (e.g. because he is already logged in)
*/
private PlayerAuth getPlayerAuth(Player player) {
return getPlayerAuth(player, false);
}
/**
* Checks the precondition for authentication (like user known) and returns
* the player's {@link PlayerAuth} object.
*
* @param player the player to check
* @param quiet don't send messages
* @return the PlayerAuth object, or {@code null} if the player doesn't exist or may not log in
* (e.g. because he is already logged in)
*/
private PlayerAuth getPlayerAuth(Player player, boolean quiet) {
final String name = player.getName().toLowerCase();
if (playerCache.isAuthenticated(name)) {
service.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
if (!quiet) {
service.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
}
return null;
}
PlayerAuth auth = dataSource.getAuth(name);
if (auth == null) {
service.send(player, MessageKey.UNKNOWN_USER);
if (!quiet) {
service.send(player, MessageKey.UNKNOWN_USER);
}
// Recreate the message task to immediately send the message again as response
limboService.resetMessageTask(player, LimboMessageType.REGISTER);
return null;
@ -143,13 +173,17 @@ public class AsynchronousLogin implements AsynchronousProcess {
if (!service.getProperty(DatabaseSettings.MYSQL_COL_GROUP).isEmpty()
&& auth.getGroupId() == service.getProperty(HooksSettings.NON_ACTIVATED_USERS_GROUP)) {
service.send(player, MessageKey.ACCOUNT_NOT_ACTIVATED);
if (!quiet) {
service.send(player, MessageKey.ACCOUNT_NOT_ACTIVATED);
}
return null;
}
final String ip = PlayerUtils.getPlayerIp(player);
if (hasReachedMaxLoggedInPlayersForIp(player, ip)) {
service.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
if (!quiet) {
service.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
}
return null;
}

View File

@ -186,8 +186,6 @@ public class GeoIpService {
* @throws IOException if failed during downloading and writing to destination file
*/
private String downloadDatabaseArchive(Instant lastModified, Path destination) throws IOException {
HttpURLConnection connection = (HttpURLConnection) new URL(ARCHIVE_URL).openConnection();
String clientId = settings.getProperty(ProtectionSettings.MAXMIND_API_CLIENT_ID);
String licenseKey = settings.getProperty(ProtectionSettings.MAXMIND_API_LICENSE_KEY);
if (clientId.isEmpty() || licenseKey.isEmpty()) {
@ -195,6 +193,8 @@ public class GeoIpService {
+ " GeoIp protections will be disabled.");
return null;
}
HttpURLConnection connection = (HttpURLConnection) new URL(ARCHIVE_URL).openConnection();
String basicAuth = "Basic " + new String(Base64.getEncoder().encode((clientId + ":" + licenseKey).getBytes()));
connection.setRequestProperty("Authorization", basicAuth);

View File

@ -4,6 +4,7 @@ import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.ProxySessionManager;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.output.ConsoleLoggerFactory;
@ -24,16 +25,18 @@ public class BungeeReceiver implements PluginMessageListener, SettingsDependent
private final AuthMe plugin;
private final BukkitService bukkitService;
private final ProxySessionManager proxySessionManager;
private final Management management;
private final DataSource dataSource;
private boolean isEnabled;
@Inject
BungeeReceiver(AuthMe plugin, BukkitService bukkitService, Management management,
DataSource dataSource, Settings settings) {
BungeeReceiver(AuthMe plugin, BukkitService bukkitService, ProxySessionManager proxySessionManager,
Management management, DataSource dataSource, Settings settings) {
this.plugin = plugin;
this.bukkitService = bukkitService;
this.proxySessionManager = proxySessionManager;
this.management = management;
this.dataSource = dataSource;
reload(settings);
@ -149,9 +152,14 @@ public class BungeeReceiver implements PluginMessageListener, SettingsDependent
private void performLogin(final String name) {
Player player = bukkitService.getPlayerExact(name);
if (player != null && player.isOnline()) {
management.forceLogin(player);
management.forceLogin(player, true);
logger.info("The user " + player.getName() + " has been automatically logged in, "
+ "as requested via plugin messaging.");
} else {
proxySessionManager.processProxySessionMessage(name);
logger.info("The user " + name + " should be automatically logged in, "
+ "as requested via plugin messaging but has not been detected, nickname has been"
+" added to autologin queue.");
}
}

View File

@ -60,10 +60,16 @@ public class SettingsMigrationService extends PlainMigrationService {
@SuppressWarnings("checkstyle:BooleanExpressionComplexity")
protected boolean performMigrations(PropertyReader reader, ConfigurationData configurationData) {
boolean changes = false;
if ("[a-zA-Z0-9_?]*".equals(reader.getString(ALLOWED_NICKNAME_CHARACTERS.getPath()))) {
configurationData.setValue(ALLOWED_NICKNAME_CHARACTERS, "[a-zA-Z0-9_]*");
changes = true;
}
String driverClass = reader.getString(DatabaseSettings.MYSQL_DRIVER_CLASS_NAME.getPath());
if ("com.mysql.jdbc.Driver".equals(driverClass) || "com.mysql.cj.jdbc.Driver".equals(driverClass)) {
configurationData.setValue(DatabaseSettings.MYSQL_DRIVER_CLASS_NAME, DatabaseSettings.MYSQL_DRIVER_CLASS_NAME.getDefaultValue());
changes = true;
}
setOldOtherAccountsCommandFieldsIfSet(reader);

View File

@ -273,7 +273,7 @@ public class SpawnLoader implements Reloadable {
* @return location of the given player if alive, spawn location if dead.
*/
public Location getPlayerLocationOrSpawn(Player player) {
if (player.isOnline() && player.isDead()) {
if (player.getHealth() <= 0.0) {
return getSpawnLocation(player);
}
return player.getLocation();

View File

@ -44,6 +44,13 @@ public final class DatabaseSettings implements SettingsHolder {
@Comment("Password to connect to the MySQL database")
public static final Property<String> MYSQL_PASSWORD =
newProperty("DataSource.mySQLPassword", "12345");
@Comment({"Driver Name of the MySQL database.",
"Built-in drivers:",
" MySQL: 'fr.xephi.authme.libs.com.mysql.cj.jdbc.Driver'",
" MariaDB: 'fr.xephi.authme.libs.org.mariadb.jdbc.Driver'"})
public static final Property<String> MYSQL_DRIVER_CLASS_NAME =
newProperty("DataSource.mySQLDriverClassName", "fr.xephi.authme.libs.com.mysql.cj.jdbc.Driver");
@Comment("Database Name, use with converters or as SQLITE database name")
public static final Property<String> MYSQL_DATABASE =

View File

@ -44,7 +44,7 @@ public final class HooksSettings implements SettingsHolder {
@Comment("How much log2 rounds needed in BCrypt (do not change if you do not know what it does)")
public static final Property<Integer> BCRYPT_LOG2_ROUND =
newProperty("ExternalBoardOptions.bCryptLog2Round", 10);
newProperty("ExternalBoardOptions.bCryptLog2Round", 12);
@Comment("phpBB table prefix defined during the phpBB installation process")
public static final Property<String> PHPBB_TABLE_PREFIX =

View File

@ -27,7 +27,7 @@ public final class RestrictionSettings implements SettingsHolder {
@Comment("Allowed commands for unauthenticated players")
public static final Property<Set<String>> ALLOW_COMMANDS =
newLowercaseStringSetProperty("settings.restrictions.allowCommands",
"/login", "/register", "/l", "/reg", "/email", "/captcha", "/2fa", "/totp");
"/login", "/log", "/l", "/register", "/reg", "/email", "/captcha", "/2fa", "/totp");
@Comment({
"Max number of allowed registrations per IP",

View File

@ -0,0 +1,349 @@
# Translation config for the AuthMe help, e.g. when /authme help or /authme help register is called
# -------------------------------------------------------
# List of texts used in the help section
common:
header: '==========[ AuthMeReloaded 幫助 ]=========='
optional: '選填'
hasPermission: '您有使用此指令的權限'
noPermission: '您沒有使用此指令的權限'
default: '預設'
result: '結果'
defaultPermissions:
notAllowed: '無權限'
opOnly: '僅限管理員'
allowed: '開放給所有人'
# -------------------------------------------------------
# Titles of the individual help sections
# Set the translation text to empty text to disable the section, e.g. to hide alternatives:
# alternatives: ''
section:
command: '指令'
description: '簡述'
detailedDescription: '詳述'
arguments: '參數'
permissions: '權限'
alternatives: '別名'
children: '指令集'
# -------------------------------------------------------
# You can translate the data for all commands using the below pattern.
# For example to translate /authme reload, create a section "authme.reload", or "login" for /login
# If the command has arguments, you can use arg1 as below to translate the first argument, and so forth
# Translations don't need to be complete; any missing section will be taken from the default silently
# Important: Put main commands like "authme" before their children (e.g. "authme.reload")
commands:
authme.register:
description: '註冊一位玩家'
detailedDescription: '以指定的密碼註冊一位玩家。'
arg1:
label: '名稱'
description: '玩家名稱'
arg2:
label: '密碼'
description: '密碼'
authme.unregister:
description: '註銷一位玩家。'
detailedDescription: '註銷指定的玩家。'
arg1:
label: '名稱'
description: '玩家名稱'
authme.forcelogin:
description: '強制玩家重新登入。'
detailedDescription: '強制指定的玩家重新登入。'
arg1:
label: '名稱'
description: '玩家名稱'
authme.password:
description: '修改玩家的密碼。'
detailedDescription: '修改指定的玩家的密碼。'
arg1:
label: '名稱'
description: '玩家名稱'
arg2:
label: '密碼'
description: '密碼'
authme.lastlogin:
description: '查看玩家最後登入的日期。'
detailedDescription: '查看指定的玩家最後登入的日期。'
arg1:
label: '名稱'
description: '玩家名稱'
authme.accounts:
description: '透過玩家名稱或 IP 顯示玩家的所有帳號。'
detailedDescription: '透過玩家名稱或 IP 顯示玩家的所有帳號。'
arg1:
label: '名稱'
description: '玩家名稱'
authme.email:
description: '顯示指定的玩家的電子郵件地址。(如果有設置的話)'
detailedDescription: '顯示指定的玩家的電子郵件地址。(如果有設置的話)'
arg1:
label: '名稱'
description: '玩家名稱'
authme.setemail:
description: '修改玩家的電子郵件地址。'
detailedDescription: '修改指定的玩家的電子郵件地址。'
arg1:
label: '名稱'
description: '玩家名稱'
arg2:
label: '電子郵件地址'
description: '電子郵件地址'
authme.getip:
description: '取得線上玩家的 IP 位址。'
detailedDescription: '取得指定的線上玩家的 IP 位址。'
arg1:
label: '名稱'
description: '玩家名稱'
authme.totp:
description: '顯示玩家是否開啟兩步驟驗證。'
detailedDescription: '顯示指定的玩家是否開啟兩步驟驗證。'
arg1:
label: '名稱'
description: '玩家名稱'
authme.disabletotp:
description: '為玩家停用兩步驟驗證。'
detailedDescription: '為指定的玩家停用兩步驟驗證。'
arg1:
label: '名稱'
description: '玩家名稱'
authme.spawn:
description: '傳送至重生點。'
detailedDescription: '傳送至重生點。'
authme.setspawn:
description: '將玩家的重生點設為您現在的位置。'
detailedDescription: '將玩家的重生點設為您現在的位置。'
authme.firstspawn:
description: '傳送至新玩家重生點。'
detailedDescription: '傳送至新玩家重生點。'
authme.setfirstspawn:
description: '將新玩家的重生點設為您現在的位置。'
detailedDescription: '將新玩家的重生點設為您現在的位置。'
authme.purge:
description: '刪除超過指定天數的 AuthMeReloaded 資料。'
detailedDescription: '刪除超過指定天數的 AuthMeReloaded 資料。'
arg1:
label: '天'
description: '天數'
authme.purgeplayer:
description: '刪除玩家資料。'
detailedDescription: '刪除指定的玩家的資料。'
arg1:
label: '名稱'
description: '玩家名稱'
arg2:
label: '選項'
description: '選項'
authme.backup:
description: '備份玩家資料。'
detailedDescription: '備份所有玩家的資料。'
authme.resetpos:
description: '重設玩家的登出前位置。'
detailedDescription: '重設指定/所有玩家的登出前位置。'
arg1:
label: '名稱/*'
description: '指定玩家/所有玩家'
authme.purgebannedplayers:
description: '刪除已被封禁的玩家的資料。'
detailedDescription: '刪除所有已被封禁的玩家的資料。'
authme.switchantibot:
description: '切換 AntiBot 的模式。'
detailedDescription: '切換 AntiBot 的模式。'
arg1:
label: '模式'
description: '模式'
authme.reload:
description: '重新載入 AuthMeReloaded。'
detailedDescription: '重新載入 AuthMeReloaded。'
authme.version:
description: '顯示 AuthMeReloaded 的詳細資訊。'
detailedDescription: '顯示 AuthMeReloaded 的詳細資訊。例如版本、開發者、貢獻者、及授權。'
authme.converter:
description: 'AuthMeReloaded 的轉換器指令。'
detailedDescription: 'AuthMeReloaded 的轉換器指令。'
arg1:
label: '工作'
description: '工作'
authme.messages:
description: '修改目前的幫助檔案。'
detailedDescription: '修改目前的幫助檔案。'
authme.recent:
description: '顯示最近登入的玩家。'
detailedDescription: '顯示最近登入的玩家。'
authme.debug:
description: '除錯。'
detailedDescription: '除錯。'
arg1:
label: '子程序'
description: '子程序'
arg2:
label: '參數'
description: '參數'
arg3:
label: '參數'
description: '參數'
authme.help:
description: '顯示 /authme 指令的詳細介紹。'
detailedDescription: '顯示 /authme 指令的詳細介紹。'
arg1:
label: '要查詢的指令'
description: '要查詢的指令'
email.show:
description: '顯示您目前的電子郵件地址。'
detailedDescription: '顯示您目前的電子郵件地址。'
email.add:
description: '新增電子郵件地址'
detailedDescription: '新增電子郵件地址至您的帳號。'
arg1:
label: '電子郵件地址'
description: '電子郵件地址'
arg2:
label: '確認電子郵件地址'
description: '確認電子郵件地址'
email.change:
description: '修改電子郵件地址'
detailedDescription: '修改您的帳號的電子郵件地址。'
arg1:
label: '舊的電子郵件地址'
description: '舊的電子郵件地址'
arg2:
label: '新的電子郵件地址'
description: '新的電子郵件地址'
email.recover:
description: '使用別的電子郵件地址復原您的帳號。'
detailedDescription: '復原您的帳號,將寄送新的密碼至您提供的電子郵件地址。'
arg1:
label: '電子郵件地址'
description: '電子郵件地址'
email.code:
description: '使用代碼復原您的帳號。'
detailedDescription: '輸入已寄送至您的電子郵件信箱的代碼以復原您的帳號。'
arg1:
label: '電子郵件地址'
description: '電子郵件地址'
email.setpassword:
description: '復原帳號後,設置新密碼。'
detailedDescription: '復原帳號後,設置新密碼。'
arg1:
label: '密碼'
description: '密碼'
email.help:
description: '顯示 /email 指令的詳細介紹。'
detailedDescription: '顯示 /email 指令的詳細介紹。'
arg1:
label: '要查詢的指令'
description: '要查詢的指令'
login:
login.help:
description: '顯示 /login 指令的詳細介紹。'
detailedDescription: '顯示 /login 指令的詳細介紹。'
arg1:
label: '要查詢的指令'
description: '要查詢的指令'
logout:
description: '登出帳號'
detailedDescription: '登出您的帳號。'
logout.help:
description: '顯示 /logout 指令的詳細介紹。'
detailedDescription: '顯示 /logout 指令的詳細介紹。'
arg1:
label: '要查詢的指令'
description: '要查詢的指令'
register:
description: '註冊帳號'
detailedDescription: '註冊您的帳號。'
arg1:
label: '密碼'
description: '密碼'
arg2:
label: '確認密碼'
description: '確認密碼'
register.help:
description: '顯示 /register 指令的詳細介紹。'
detailedDescription: '顯示 /register 指令的詳細介紹。'
arg1:
label: '要查詢的指令'
description: '要查詢的指令'
unregister:
description: '註銷帳號'
detailedDescription: '註銷您的帳號。'
arg1:
label: '密碼'
description: '密碼'
unregister.help:
description: '顯示 /unregister 指令的詳細介紹。'
detailedDescription: '顯示 /unregister 指令的詳細介紹。'
arg1:
label: '要查詢的指令'
description: '要查詢的指令'
changepassword:
description: '修改密碼'
detailedDescription: '修改您的密碼。'
arg1:
label: '舊密碼'
description: '舊密碼'
arg2:
label: '新密碼'
description: '新密碼'
changepassword.help:
description: '顯示 /changepassword 指令的詳細介紹。'
detailedDescription: '顯示 /changepassword 指令的詳細介紹。'
arg1:
label: '要查詢的指令'
description: '要查詢的指令'
totp:
description: '執行兩步驟驗證的相關操作。'
detailedDescription: '執行兩步驟驗證的相關操作。'
totp.code:
description: '登入時,輸入您的兩步驟驗證碼。'
detailedDescription: '登入時,輸入您的兩步驟驗證碼。'
arg1:
label: '驗證碼'
description: '驗證碼'
totp.add:
description: '為您的帳號啟用兩步驟驗證。'
detailedDescription: '為您的帳號啟用兩步驟驗證。'
totp.confirm:
description: '確認後,儲存生成的兩步驟驗證金鑰。'
detailedDescription: '確認後,儲存生成的兩步驟驗證金鑰。'
arg1:
label: '驗證碼'
description: '驗證碼'
totp.remove:
description: '為您的帳號停用兩步驟驗證。'
detailedDescription: '為您的帳號停用兩步驟驗證。'
arg1:
label: '驗證碼'
description: '驗證碼'
totp.help:
description: '顯示 /totp 指令的詳細介紹。'
detailedDescription: '顯示 /totp 指令的詳細介紹。'
arg1:
label: '要查詢的指令'
description: '要查詢的指令'
captcha:
description: 'AuthMeReloaded 的 Captcha 指令。'
detailedDescription: 'AuthMeReloaded 的 Captcha 指令。'
arg1:
label: 'Captcha 碼'
description: 'Captcha 碼'
captcha.help:
description: '顯示 /captcha 指令的詳細介紹。'
detailedDescription: '顯示 /captcha 指令的詳細介紹。'
arg1:
label: '要查詢的指令'
description: '要查詢的指令'
verification:
description: '完成驗證過程的指令。'
detailedDescription: '完成驗證過程的指令。'
arg1:
label: '驗證碼'
description: '驗證碼'
verification.help:
description: '顯示 /verification 指令的詳細介紹。'
detailedDescription: '顯示 /verification 指令的詳細介紹。'
arg1:
label: '要查詢的指令'
description: '要查詢的指令'

View File

@ -40,7 +40,7 @@ error:
max_registration: '&cPrzekroczyłeś limit zarejestrowanych kont na serwerze &8(&e%reg_count/%max_acc %reg_names&8) &cdla twojego połączenia.'
logged_in: '&fJesteś już zalogowany!'
kick_for_vip: '&cGracz VIP dołączył do gry!'
# TODO kick_unresolved_hostname: '&cAn error occurred: unresolved player hostname!'
kick_unresolved_hostname: '&cWystąpił błąd: nierozwiązana nazwa hosta gracza!'
tempban_max_logins: '&cZostałeś tymczasowo zbanowany za dużą liczbę nieudanych logowań!'
# AntiBot

View File

@ -40,7 +40,7 @@ error:
max_registration: '&cBạn đã vượt quá giới hạn tối đa đăng ký tài khoản (%reg_count/%max_acc %reg_names) cho những lần kết nối tài khoản!'
logged_in: '&cBạn đã đăng nhập!'
kick_for_vip: '&eChỉ có thành viên VIP mới được tham gia khi máy chủ đầy!'
# TODO kick_unresolved_hostname: '&cAn error occurred: unresolved player hostname!'
kick_unresolved_hostname: '&cLôi đã xảy ra: Không thể phân giải hostname!'
tempban_max_logins: '&cBạn đã bị tạm thời khóa truy cập do đăng nhập sai nhiều lần.'
# AntiBot
@ -79,7 +79,7 @@ on_join_validation:
country_banned: '&4Quốc gia của bạn bị cấm tham gia máy chủ này!'
not_owner_error: 'Bạn không phải là chủ sở hữu tài khoản này, hãy chọn tên khác!'
invalid_name_case: 'Bạn nên vào máy chủ với tên đăng nhập là %valid, không phải là %invalid.'
# TODO quick_command: 'You used a command too fast! Please, join the server again and wait more before using any command.'
quick_command: 'Bạn đang xài lệnh quá nhanh. Hãy thoát máy chủ và chờ một lúc trước khi sử dụng lệnh'
# Email
email:
@ -90,10 +90,10 @@ email:
old_email_invalid: '&cEmail cũ không hợp lệ, vui lòng thử lại!'
invalid: '&cĐại chỉ email không hợp lệ, vui lòng thử lại!'
added: '&2Địa chỉ email đã thêm vào tài khoản của bạn thành công!'
# TODO add_not_allowed: '&cAdding email was not allowed'
add_not_allowed: '&cViệc thêm email không được cho phép!'
request_confirmation: '&cVui lòng xác nhận địa chỉ email của bạn!'
changed: '&2Địa chỉ email đã thay đổi!'
# TODO change_not_allowed: '&cChanging email was not allowed'
change_not_allowed: '&cViệc đổi địa chỉ email không được cho phép'
email_show: '&2Địa chỉ email hiện tại của bạn là: &f%email'
no_email_for_account: '&2Hiện tại bạn chưa liên kết bất kỳ email nào với tài khoản này.'
already_used: '&4Địa chỉ email đã được sử dụng'
@ -110,9 +110,9 @@ recovery:
code:
code_sent: 'Một mã khôi phục mật khẩu đã được gửi đến địa chỉ email của bạn.'
incorrect: 'Mã khôi phục không đúng! Dùng lệnh /email recovery [email] để tạo một mã mới'
# TODO tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.'
# TODO correct: 'Recovery code entered correctly!'
# TODO change_password: 'Please use the command /email setpassword <new password> to change your password immediately.'
tries_exceeded: 'Bạn đã vượt quá số lần tối đa cho phép nhập mã khôi phục. Hãy sử dụng lệnh "/email recovery [email]" để tạo một mã mới.'
correct: 'Mã khôi phục đã được nhập chính xác!'
change_password: 'Hãy sử dụng lệnh "/email setpassword <mật khẩu mới>" để đổi mật khẩu của bạn ngay lập tức.'
# Captcha
captcha:
@ -125,12 +125,12 @@ captcha:
# Verification code
verification:
# TODO code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO command_usage: '&cUsage: /verification <code>'
command_usage: '&cLệnh: /verification <code>'
# TODO incorrect_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO success: '&2Your identity has been verified! You can now execute all commands within the current session!'
success: '&2Danh tính của bạn đã được xác minh! Bạn đã có thể sử dụng các lệnh trong phiên đăng nhập này'
# TODO already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO code_expired: '&3Your code has expired! Execute another sensitive command to get a new code!'
# TODO email_needed: '&3To verify your identity you need to link an email address with your account!!'
email_needed: '&3Để xác định danh tính của bạn, bạn cần kết nối tài khoản này với 1 email!'
# Time units
time:
@ -146,12 +146,13 @@ time:
# Two-factor authentication
two_factor:
code_created: '&2Mã bí mật của bạn là %code. Bạn có thể quét nó tại đây %url'
# TODO confirmation_required: 'Please confirm your code with /2fa confirm <code>'
# TODO code_required: 'Please submit your two-factor authentication code with /2fa code <code>'
# TODO already_enabled: 'Two-factor authentication is already enabled for your account!'
# TODO enable_error_no_code: 'No 2fa key has been generated for you or it has expired. Please run /2fa add'
# TODO enable_success: 'Successfully enabled two-factor authentication for your account'
# TODO enable_error_wrong_code: 'Wrong code or code has expired. Please run /2fa add'
# TODO not_enabled_error: 'Two-factor authentication is not enabled for your account. Run /2fa add'
# TODO removed_success: 'Successfully removed two-factor auth from your account'
# TODO invalid_code: 'Invalid code!'
confirmation_required: 'Hãy xác thực mã của bạn bằng lệnh /2fa confirm <mã>'
code_required: 'Hãy gửi đi mã xác thực 2 lớp của bạn bằng lệnh /2fa code <mã>'
already_enabled: 'Xác thực 2 lớp đã được kích hoạt trên tài khoản của bạn!'
enable_error_no_code: 'Không có mã xác thực nào đã được tạo cho tài khoản của bạn hoặc nó đã hết hạn. Hãy sử dụng lệnh /2fa add'
enable_success: 'Đã thành công kích hoạt xác thực 2 lớp cho tài khoản của bạn!'
enable_error_wrong_code: 'Mã xác thực bị sai hoặc đã bị hết hạn. Hãy sử dụng lệnh /2fa add'
not_enabled_error: 'Xác thực 2 lớp không được kích hoạt trên tài khoản của bạn. Hãy sử dụng lệnh /2fa add'
removed_success: 'Đã thành công tắt xác thực 2 lớp khỏi tài khoản của bạn'
invalid_code: 'Mã xác thực không hợp lệ!'

View File

@ -2,7 +2,6 @@
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Edited: 2020/11/26 Leonard Jiang @ Waveland
# Registration
registration:
@ -20,7 +19,7 @@ password:
name_in_password: '&8[&6玩家系统&8] &f你不能使用你的名字作为密码。 '
unsafe_password: '&8[&6玩家系统&8] &f你不能使用安全性过低的密码。 '
forbidden_characters: '&4您的密码包含了非法字符。可使用的字符: %valid_chars'
wrong_length: '&8[&6玩家系统&8] &你的密码不符合要求'
wrong_length: '&8[&6玩家系统&8] &c你的密码不符合要求'
# Login
login:

View File

@ -71,11 +71,11 @@ public class AuthMeInitializationTest {
public void initAuthMe() throws IOException {
dataFolder = temporaryFolder.newFolder();
File settingsFile = new File(dataFolder, "config.yml");
given(server.getLogger()).willReturn(Logger.getAnonymousLogger());
JavaPluginLoader pluginLoader = new JavaPluginLoader(server);
Files.copy(TestHelper.getJarFile(TestHelper.PROJECT_ROOT + "config.test.yml"), settingsFile);
// Mock / wire various Bukkit components
given(server.getLogger()).willReturn(Logger.getAnonymousLogger());
ReflectionTestUtils.setField(Bukkit.class, null, "server", server);
given(server.getPluginManager()).willReturn(pluginManager);

View File

@ -6,9 +6,12 @@ import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
/**
@ -21,12 +24,12 @@ public final class LimboPlayerMatchers {
}
public static Matcher<LimboPlayer> isLimbo(LimboPlayer limbo) {
String[] groups = limbo.getGroups().toArray(new String[limbo.getGroups().size()]);
UserGroup[] groups = limbo.getGroups().toArray(new UserGroup[limbo.getGroups().size()]);
return isLimbo(limbo.isOperator(), limbo.isCanFly(), limbo.getWalkSpeed(), limbo.getFlySpeed(), groups);
}
public static Matcher<LimboPlayer> isLimbo(boolean isOp, boolean canFly, float walkSpeed, float flySpeed,
String... groups) {
UserGroup... groups) {
return new TypeSafeMatcher<LimboPlayer>() {
@Override
protected boolean matchesSafely(LimboPlayer item) {
@ -39,14 +42,19 @@ public final class LimboPlayerMatchers {
@Override
public void describeTo(Description description) {
List<String> groupNames = Arrays.stream(groups).map(UserGroup::getGroupName).collect(toList());
description.appendText(format("Limbo with isOp=%s, groups={%s}, canFly=%s, walkSpeed=%f, flySpeed=%f",
isOp, String.join(" ,", groups), canFly, walkSpeed, flySpeed));
isOp, String.join(" ,", groupNames), canFly, walkSpeed, flySpeed));
}
@Override
public void describeMismatchSafely(LimboPlayer item, Description description) {
List<String> groupNames = item.getGroups().stream()
.map(UserGroup::getGroupName)
.collect(toList());
description.appendText(format("Limbo with isOp=%s, groups={%s}, canFly=%s, walkSpeed=%f, flySpeed=%f",
item.isOperator(), String.join(" ,", item.getGroups()), item.isCanFly(),
item.isOperator(), String.join(" ,", groupNames), item.isCanFly(),
item.getWalkSpeed(), item.getFlySpeed()));
}
};
@ -119,7 +127,7 @@ public final class LimboPlayerMatchers {
}
// Hamcrest's contains() doesn't like it when there are no items, so we need to check for the empty case explicitly
private static boolean collectionContains(Collection<String> givenItems, String... expectedItems) {
private static boolean collectionContains(Collection<UserGroup> givenItems, UserGroup... expectedItems) {
if (expectedItems.length == 0) {
return givenItems.isEmpty();
}

View File

@ -106,7 +106,7 @@ public class LimboPlayerTaskManagerTest {
String name = "rats";
Player player = mock(Player.class);
given(player.getName()).willReturn(name);
LimboPlayer limboPlayer = new LimboPlayer(null, true, Collections.singletonList("grp"), false, 0.1f, 0.0f);
LimboPlayer limboPlayer = new LimboPlayer(null, true, Collections.singletonList(new UserGroup("grp")), false, 0.1f, 0.0f);
MessageTask existingMessageTask = mock(MessageTask.class);
limboPlayer.setMessageTask(existingMessageTask);
given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(8);
@ -129,7 +129,7 @@ public class LimboPlayerTaskManagerTest {
String name = "race";
Player player = mock(Player.class);
given(player.getName()).willReturn(name);
LimboPlayer limboPlayer = new LimboPlayer(null, true, Collections.singletonList("grp"), false, 0.1f, 0.0f);
LimboPlayer limboPlayer = new LimboPlayer(null, true, Collections.singletonList(new UserGroup("grp")), false, 0.1f, 0.0f);
given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(12);
given(registrationCaptchaManager.isCaptchaRequired(name)).willReturn(true);
String captcha = "M032";

View File

@ -37,9 +37,9 @@ public class LimboServiceHelperTest {
public void shouldMergeLimboPlayers() {
// given
Location newLocation = mock(Location.class);
LimboPlayer newLimbo = new LimboPlayer(newLocation, false, Collections.singletonList("grp-new"), false, 0.0f, 0.0f);
LimboPlayer newLimbo = new LimboPlayer(newLocation, false, Collections.singletonList(new UserGroup("grp-new")), false, 0.0f, 0.0f);
Location oldLocation = mock(Location.class);
LimboPlayer oldLimbo = new LimboPlayer(oldLocation, true, Collections.singletonList("grp-old"), true, 0.1f, 0.8f);
LimboPlayer oldLimbo = new LimboPlayer(oldLocation, true, Collections.singletonList(new UserGroup("grp-old")), true, 0.1f, 0.8f);
// when
LimboPlayer result = limboServiceHelper.merge(newLimbo, oldLimbo);
@ -47,7 +47,7 @@ public class LimboServiceHelperTest {
// then
assertThat(result.getLocation(), equalTo(oldLocation));
assertThat(result.isOperator(), equalTo(true));
assertThat(result.getGroups(), contains("grp-old"));
assertThat(result.getGroups(), contains(new UserGroup("grp-old")));
assertThat(result.isCanFly(), equalTo(true));
assertThat(result.getWalkSpeed(), equalTo(0.1f));
assertThat(result.getFlySpeed(), equalTo(0.8f));
@ -57,7 +57,7 @@ public class LimboServiceHelperTest {
public void shouldFallBackToNewLimboForMissingData() {
// given
Location newLocation = mock(Location.class);
LimboPlayer newLimbo = new LimboPlayer(newLocation, false, Collections.singletonList("grp-new"), true, 0.3f, 0.0f);
LimboPlayer newLimbo = new LimboPlayer(newLocation, false, Collections.singletonList(new UserGroup("grp-new")), true, 0.3f, 0.0f);
LimboPlayer oldLimbo = new LimboPlayer(null, false, Collections.emptyList(), false, 0.1f, 0.1f);
// when
@ -66,7 +66,7 @@ public class LimboServiceHelperTest {
// then
assertThat(result.getLocation(), equalTo(newLocation));
assertThat(result.isOperator(), equalTo(false));
assertThat(result.getGroups(), contains("grp-new"));
assertThat(result.getGroups(), contains(new UserGroup("grp-new")));
assertThat(result.isCanFly(), equalTo(true));
assertThat(result.getWalkSpeed(), equalTo(0.3f));
assertThat(result.getFlySpeed(), equalTo(0.1f));

View File

@ -83,7 +83,7 @@ public class LimboServiceTest {
Location playerLoc = mock(Location.class);
given(spawnLoader.getPlayerLocationOrSpawn(player)).willReturn(playerLoc);
given(permissionsManager.hasGroupSupport()).willReturn(true);
given(permissionsManager.getGroups(player)).willReturn(Collections.singletonList("permgrwp"));
given(permissionsManager.getGroups(player)).willReturn(Collections.singletonList(new UserGroup("permgrwp")));
given(settings.getProperty(LimboSettings.RESTORE_ALLOW_FLIGHT)).willReturn(AllowFlightRestoreType.ENABLE);
// when
@ -105,7 +105,7 @@ public class LimboServiceTest {
assertThat(limbo.isCanFly(), equalTo(false));
assertThat(limbo.getFlySpeed(), equalTo(0.2f));
assertThat(limbo.getLocation(), equalTo(playerLoc));
assertThat(limbo.getGroups(), equalTo(Collections.singletonList("permgrwp")));
assertThat(limbo.getGroups(), equalTo(Collections.singletonList(new UserGroup("permgrwp"))));
}
@Test
@ -241,7 +241,7 @@ public class LimboServiceTest {
return player;
}
private static LimboPlayer convertToLimboPlayer(Player player, Location location, Collection<String> groups) {
private static LimboPlayer convertToLimboPlayer(Player player, Location location, Collection<UserGroup> groups) {
return new LimboPlayer(location, player.isOp(), groups, player.getAllowFlight(),
player.getWalkSpeed(), player.getFlySpeed());
}

View File

@ -6,6 +6,7 @@ import ch.jalu.injector.testing.InjectDelayed;
import com.google.common.io.Files;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.data.limbo.LimboPlayer;
import fr.xephi.authme.data.limbo.UserGroup;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.settings.Settings;
@ -48,22 +49,22 @@ public class DistributedFilesPersistenceHandlerTest {
/** Player is in seg32-10110 and should be migrated into seg16-f. */
private static final UUID MIGRATED_UUID = fromString("f6a97c88-7c8f-c12e-4931-6206d4ca067d");
private static final Matcher<LimboPlayer> MIGRATED_LIMBO_MATCHER =
isLimbo(false, true, 0.2f, 0.1f, "noob");
isLimbo(false, true, 0.2f, 0.1f, new UserGroup("noob"));
/** Existing player in seg16-f. */
private static final UUID UUID_FAB69 = fromString("fab69c88-2cd0-1fed-f00d-dead14ca067d");
private static final Matcher<LimboPlayer> FAB69_MATCHER =
isLimbo(false, false, 0.2f, 0.1f, "");
isLimbo(false, false, 0.2f, 0.1f, new UserGroup(""));
/** Player in seg16-8. */
private static final UUID UUID_STAFF = fromString("88897c88-7c8f-c12e-4931-6206d4ca067d");
private static final Matcher<LimboPlayer> STAFF_MATCHER =
isLimbo(true, false, 0.3f, 0.1f, "staff", "mod");
isLimbo(true, false, 0.3f, 0.1f, new UserGroup("staff"), new UserGroup("mod"));
/** Player in seg16-8. */
private static final UUID UUID_8C679 = fromString("8c679491-1234-abcd-9102-1fa6e0cc3f81");
private static final Matcher<LimboPlayer> SC679_MATCHER =
isLimbo(false, true, 0.1f, 0.0f, "primary");
isLimbo(false, true, 0.1f, 0.0f, new UserGroup("primary"));
/** UUID for which no data is stored (belongs to a segment file that does not exist, seg16-4). */
private static final UUID UNKNOWN_UUID = fromString("42d1cc0b-8f12-d04a-e7ba-a067d05cdc39");
@ -156,7 +157,7 @@ public class DistributedFilesPersistenceHandlerTest {
// given
Player uuidToAdd1 = mockPlayerWithUuid(UNKNOWN_UUID);
Location location1 = mockLocation("1world");
LimboPlayer limbo1 = new LimboPlayer(location1, false, Collections.singletonList("group-1"), true, 0.1f, 0.2f);
LimboPlayer limbo1 = new LimboPlayer(location1, false, Collections.singletonList(new UserGroup("group-1")), true, 0.1f, 0.2f);
Player uuidToAdd2 = mockPlayerWithUuid(UNKNOWN_UUID2);
Location location2 = mockLocation("2world");
LimboPlayer limbo2 = new LimboPlayer(location2, true, Collections.emptyList(), false, 0.0f, 0.25f);

View File

@ -5,6 +5,7 @@ import ch.jalu.injector.testing.DelayedInjectionRunner;
import ch.jalu.injector.testing.InjectDelayed;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.data.limbo.LimboPlayer;
import fr.xephi.authme.data.limbo.UserGroup;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.util.FileUtils;
@ -80,7 +81,7 @@ public class IndividualFilesPersistenceHandlerTest {
assertThat(data.isCanFly(), equalTo(true));
assertThat(data.getWalkSpeed(), equalTo(0.2f));
assertThat(data.getFlySpeed(), equalTo(0.1f));
assertThat(data.getGroups(), contains("players"));
assertThat(data.getGroups(), contains(new UserGroup("players")));
Location location = data.getLocation();
assertThat(location.getX(), equalTo(-113.219));
assertThat(location.getY(), equalTo(72.0));
@ -114,7 +115,7 @@ public class IndividualFilesPersistenceHandlerTest {
World world = mock(World.class);
given(world.getName()).willReturn("player-world");
Location location = new Location(world, 0.2, 102.25, -89.28, 3.02f, 90.13f);
LimboPlayer limbo = new LimboPlayer(location, true, Collections.singletonList("primary-grp"), true, 1.2f, 0.8f);
LimboPlayer limbo = new LimboPlayer(location, true, Collections.singletonList(new UserGroup("primary-grp")), true, 1.2f, 0.8f);
// when
handler.saveLimboPlayer(player, limbo);

View File

@ -2,7 +2,6 @@ package fr.xephi.authme.events;
import fr.xephi.authme.ClassCollector;
import fr.xephi.authme.TestHelper;
import org.apache.commons.lang.reflect.MethodUtils;
import org.bukkit.event.Event;
import org.junit.BeforeClass;
import org.junit.Test;
@ -49,7 +48,11 @@ public class EventsConsistencyTest {
@Test
public void shouldHaveStaticEventHandlerMethod() {
for (Class<?> clazz : classes) {
Method handlerListMethod = MethodUtils.getAccessibleMethod(clazz, "getHandlerList", new Class<?>[]{});
Method handlerListMethod = null;
try {
handlerListMethod = clazz.getMethod("getHandlerList");
} catch (NoSuchMethodException ignored) {
}
if (canBeInstantiated(clazz)) {
assertThat("Class " + clazz.getSimpleName() + " has static method getHandlerList()",
handlerListMethod != null && Modifier.isStatic(handlerListMethod.getModifiers()), equalTo(true));

View File

@ -65,7 +65,7 @@ import java.util.UUID;
import static com.google.common.collect.Sets.newHashSet;
import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock;
import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToScheduleSyncDelayedTaskWithDelay;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
@ -345,7 +345,7 @@ public class PlayerListenerTest {
// message sender + 3 recipients = 4
verify(listenerService, times(4)).shouldCancelEvent(any(Player.class));
verify(event, never()).setCancelled(anyBoolean());
assertThat(event.getRecipients(), contains(recipients.get(1), recipients.get(2)));
assertThat(event.getRecipients(), containsInAnyOrder(recipients.get(1), recipients.get(2)));
}
@Test

View File

@ -1,12 +1,15 @@
package fr.xephi.authme.permission.handlers;
import fr.xephi.authme.data.limbo.UserGroup;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.junit.Test;
import java.util.List;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.assertThat;
@ -29,10 +32,11 @@ public class VaultHandlerTest {
given(permissionMock.getPlayerGroups(null, player)).willReturn(new String[]{"abc", "test"});
// when
List<String> result = vaultHandlerTest.getGroups(player);
List<UserGroup> result = vaultHandlerTest.getGroups(player);
// then
assertThat(result, contains("abc", "test"));
List<String> groupNames = result.stream().map(UserGroup::getGroupName).collect(toList());
assertThat(groupNames, contains("abc", "test"));
verify(permissionMock).getPlayerGroups(null, player);
}
@ -47,7 +51,7 @@ public class VaultHandlerTest {
given(permissionMock.getPlayerGroups(null, player)).willReturn(null);
// when
List<String> result = vaultHandlerTest.getGroups(player);
List<UserGroup> result = vaultHandlerTest.getGroups(player);
// then
assertThat(result, empty());

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.process.email;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource;
@ -10,6 +11,7 @@ import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.service.bungeecord.BungeeSender;
import org.bukkit.entity.Player;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
@ -56,6 +58,11 @@ public class AsyncChangeEmailTest {
@Mock
private BukkitService bukkitService;
@BeforeClass
public static void setUp() {
TestHelper.setupLogger();
}
@Test
public void shouldChangeEmail() {
// given