Sync repo files to b22
This commit is contained in:
parent
ed320196fe
commit
3f6a7ce48f
203
.checkstyle.xml
Normal file
203
.checkstyle.xml
Normal file
@ -0,0 +1,203 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||
<!--
|
||||
Note: Checkstyle likes to introduce breaking changes like removing support for certain properties. Since this
|
||||
configuration is also used by CodeClimate, we should keep it in sync with the version that they use:
|
||||
https://github.com/codeclimate/codeclimate-checkstyle/blob/master/bin/install-checkstyle.sh
|
||||
https://docs.codeclimate.com/docs/checkstyle (currently outdated)
|
||||
-->
|
||||
|
||||
<module name="Checker">
|
||||
<property name="charset" value="UTF-8"/>
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="fileExtensions" value="java"/>
|
||||
|
||||
<module name="SuppressWarningsFilter" />
|
||||
|
||||
<module name="LineLength">
|
||||
<property name="max" value="120"/>
|
||||
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
|
||||
</module>
|
||||
|
||||
<module name="TreeWalker">
|
||||
<module name="SuppressWarningsHolder"/>
|
||||
<module name="OuterTypeFilename"/>
|
||||
<module name="IllegalTokenText">
|
||||
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||
<property name="format" value="\\u00(08|09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
|
||||
<property name="message" value="Avoid using corresponding octal or Unicode escape."/>
|
||||
</module>
|
||||
<module name="AvoidEscapedUnicodeCharacters">
|
||||
<property name="allowEscapesForControlCharacters" value="true"/>
|
||||
<property name="allowByTailComment" value="true"/>
|
||||
<property name="allowNonPrintableEscapes" value="true"/>
|
||||
</module>
|
||||
<module name="TodoComment">
|
||||
<!-- Allow comments which have an issue number as they are accounted for in the issue tracker -->
|
||||
<property name="format" value="TODO(?! #\d+:)|FIXME"/>
|
||||
</module>
|
||||
<module name="GenericWhitespace"/>
|
||||
<module name="WhitespaceAfter"/>
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="RedundantImport"/>
|
||||
<module name="UnusedImports"/>
|
||||
<module name="OneTopLevelClass"/>
|
||||
<module name="FinalClass"/>
|
||||
<module name="HideUtilityClassConstructor"/>
|
||||
<module name="InnerTypeLast"/>
|
||||
<module name="VisibilityModifier"/>
|
||||
<module name="AvoidNestedBlocks"/>
|
||||
<module name="NoLineWrap"/>
|
||||
<module name="EmptyBlock">
|
||||
<property name="option" value="TEXT"/>
|
||||
<property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
|
||||
</module>
|
||||
<module name="NeedBraces"/>
|
||||
<module name="RightCurly"/>
|
||||
<module name="RightCurly">
|
||||
<property name="option" value="alone"/>
|
||||
<property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO, STATIC_INIT, INSTANCE_INIT"/>
|
||||
</module>
|
||||
<module name="OneStatementPerLine"/>
|
||||
<module name="MultipleVariableDeclarations"/>
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<module name="MissingSwitchDefault"/>
|
||||
<module name="DefaultComesLast"/>
|
||||
<module name="FallThrough"/>
|
||||
<module name="NestedTryDepth"/>
|
||||
<module name="UpperEll"/>
|
||||
<module name="ModifierOrder"/>
|
||||
<module name="RedundantModifier"/>
|
||||
<module name="EmptyLineSeparator">
|
||||
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="tokens" value="DOT"/>
|
||||
<property name="option" value="nl"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="tokens" value="COMMA"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="PackageName">
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Package name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="TypeName">
|
||||
<message key="name.invalidPattern"
|
||||
value="Type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="MemberName">
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Member name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="ParameterName">
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="LocalVariableName">
|
||||
<property name="tokens" value="VARIABLE_DEF"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
|
||||
<property name="allowOneCharVarInForLoop" value="true"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="ClassTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Class type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="MethodTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Method type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="NoFinalizer"/>
|
||||
<module name="AbbreviationAsWordInName">
|
||||
<property name="ignoreFinal" value="false"/>
|
||||
<property name="allowedAbbreviationLength" value="1"/>
|
||||
</module>
|
||||
<module name="DeclarationOrder"/>
|
||||
<module name="OverloadMethodsDeclarationOrder"/>
|
||||
<module name="VariableDeclarationUsageDistance"/>
|
||||
<module name="MethodParamPad"/>
|
||||
<module name="StringLiteralEquality"/>
|
||||
<module name="BooleanExpressionComplexity">
|
||||
<property name="max" value="5"/>
|
||||
</module>
|
||||
<module name="OperatorWrap">
|
||||
<property name="option" value="NL"/>
|
||||
<property name="tokens" value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR "/>
|
||||
</module>
|
||||
<module name="CyclomaticComplexity">
|
||||
<property name="max" value="15"/>
|
||||
</module>
|
||||
<module name="JavaNCSS">
|
||||
<property name="methodMaximum" value="40"/>
|
||||
<property name="classMaximum" value="1000"/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="tokens" value="VARIABLE_DEF"/>
|
||||
<property name="allowSamelineMultipleAnnotations" value="true"/>
|
||||
</module>
|
||||
<module name="NonEmptyAtclauseDescription"/>
|
||||
<module name="JavadocTagContinuationIndentation">
|
||||
<property name="offset" value="2"/>
|
||||
</module>
|
||||
<module name="AtclauseOrder">
|
||||
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||
</module>
|
||||
<module name="JavadocMethod">
|
||||
<property name="allowedAnnotations" value="Override, EventHandler"/>
|
||||
<property name="tokens" value="METHOD_DEF, ANNOTATION_FIELD_DEF"/> <!-- exclude CTOR_DEF -->
|
||||
</module>
|
||||
<module name="MissingJavadocMethod">
|
||||
<property name="scope" value="package"/>
|
||||
<property name="minLineCount" value="4"/>
|
||||
<property name="allowedAnnotations" value="Override, EventHandler"/>
|
||||
<property name="tokens" value="METHOD_DEF, ANNOTATION_FIELD_DEF"/> <!-- exclude CTOR_DEF -->
|
||||
</module>
|
||||
<module name="MissingJavadocMethod">
|
||||
<property name="scope" value="private"/>
|
||||
<property name="minLineCount" value="16"/>
|
||||
<property name="allowedAnnotations" value="Override, EventHandler"/>
|
||||
<property name="tokens" value="METHOD_DEF, ANNOTATION_FIELD_DEF"/> <!-- exclude CTOR_DEF -->
|
||||
</module>
|
||||
<!-- TODO Checkstyle/#4089: need "allowedAnnotations" property to skip @Comment fields
|
||||
<module name="JavadocVariable">
|
||||
<property name="scope" value="package"/>
|
||||
<property name="tokens" value="VARIABLE_DEF"/>
|
||||
</module>
|
||||
-->
|
||||
<module name="MethodName">
|
||||
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Method name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="SingleLineJavadoc">
|
||||
<property name="ignoredTags" value="@return"/>
|
||||
</module>
|
||||
<module name="EmptyCatchBlock">
|
||||
<property name="exceptionVariableName" value="ignore|ignored"/>
|
||||
</module>
|
||||
<module name="MissingOverride"/>
|
||||
<module name="EqualsHashCode"/>
|
||||
<module name="EqualsAvoidNull"/>
|
||||
<module name="Regexp">
|
||||
<property name="format" value="\.to(Lower|Upper)Case\(\)"/>
|
||||
<property name="illegalPattern" value="true"/>
|
||||
<property name="ignoreComments" value="true"/>
|
||||
<property name="message" value="Use toLowerCase/toUpperCase with Locale"/>
|
||||
</module>
|
||||
</module>
|
||||
<module name="FileTabCharacter"/>
|
||||
</module>
|
||||
44
.codeclimate.yml
Normal file
44
.codeclimate.yml
Normal file
@ -0,0 +1,44 @@
|
||||
version: '2' # required to adjust maintainability checks
|
||||
|
||||
plugins:
|
||||
checkstyle:
|
||||
enabled: true
|
||||
config:
|
||||
file: '.checkstyle.xml'
|
||||
|
||||
checks:
|
||||
# We disable all the following CodeClimate checks: Checkstyle already checks for these things and has the advantage
|
||||
# that the Checkstyle config can also be used in one's IDE.
|
||||
argument-count:
|
||||
enabled: false
|
||||
complex-logic:
|
||||
enabled: false
|
||||
file-lines:
|
||||
enabled: false
|
||||
method-complexity:
|
||||
enabled: false
|
||||
method-count:
|
||||
enabled: false
|
||||
method-lines:
|
||||
enabled: false
|
||||
nested-control-flow:
|
||||
enabled: false
|
||||
return-statements:
|
||||
enabled: false
|
||||
similar-code:
|
||||
enabled: false
|
||||
# The "identical-code" check would be cool to enable since Checkstyle offers no such functionality, but it is
|
||||
# too aggressive and we'd have to suppress many reported warnings.
|
||||
identical-code:
|
||||
enabled: false
|
||||
|
||||
exclude_patterns:
|
||||
# Exclude code from third-party sources
|
||||
- 'src/main/java/fr/xephi/authme/mail/OAuth2Provider.java'
|
||||
- 'src/main/java/fr/xephi/authme/mail/OAuth2SaslClient.java'
|
||||
- 'src/main/java/fr/xephi/authme/mail/OAuth2SaslClientFactory.java'
|
||||
- 'src/main/java/fr/xephi/authme/security/crypts/PhpBB.java'
|
||||
- 'src/main/java/fr/xephi/authme/security/crypts/Whirlpool.java'
|
||||
- 'src/main/java/fr/xephi/authme/security/crypts/Wordpress.java'
|
||||
# Don't check test classes
|
||||
- 'src/test/java/**/*Test.java'
|
||||
13
.editorconfig
Normal file
13
.editorconfig
Normal file
@ -0,0 +1,13 @@
|
||||
# Top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
# Set the charset, and space indention
|
||||
[*.java]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
25
.github/ISSUE_TEMPLATE.MD
vendored
Normal file
25
.github/ISSUE_TEMPLATE.MD
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
#### 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
83
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal 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
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal 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.
|
||||
12
.github/dependabot.yml
vendored
Normal file
12
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: maven
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
- dependency-name: com.google.code.gson:gson
|
||||
- dependency-name: com.google.guava:guava
|
||||
- dependency-name: org.apache.logging.log4j:log4j-core
|
||||
- dependency-name: com.zaxxer:HikariCP
|
||||
23
.github/workflows/maven.yml
vendored
Normal file
23
.github/workflows/maven.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
name: Java CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
strategy:
|
||||
matrix:
|
||||
jdkversion: [11]
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: ${{ matrix.jdkversion }}
|
||||
cache: 'maven'
|
||||
- name: 使用Maven构建
|
||||
run: mvn -V -B clean package --file pom.xml
|
||||
115
.gitignore
vendored
Normal file
115
.gitignore
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
### Java files ###
|
||||
*.class
|
||||
MANIFEST.MF
|
||||
|
||||
# Package Files
|
||||
#*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
# Mac OS
|
||||
.DS_Store
|
||||
|
||||
### Intellij ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
|
||||
# Ignore project files
|
||||
*.iml
|
||||
*.java___jb_tmp___
|
||||
|
||||
# Ignore IDEA directory
|
||||
.idea/*
|
||||
|
||||
# Include the project's code style settings file
|
||||
!.idea/codeStyleSettings.xml
|
||||
|
||||
# File-based project format:
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
### Plugin-specific files: ###
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
|
||||
|
||||
|
||||
### Eclipse ###
|
||||
*.pydevproject
|
||||
.metadata
|
||||
.gradle
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# Eclipse Core
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
|
||||
|
||||
### Maven ###
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
|
||||
|
||||
|
||||
### NetBeans ###
|
||||
nbproject/private/
|
||||
build/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
nbactions.xml
|
||||
nb-configuration.xml
|
||||
.nb-gradle/
|
||||
|
||||
|
||||
|
||||
### Git ###
|
||||
# Don't exclude the .gitignore itself
|
||||
!.gitignore
|
||||
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
291
.idea/codeStyleSettings.xml
generated
Normal file
291
.idea/codeStyleSettings.xml
generated
Normal file
@ -0,0 +1,291 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectCodeStyleSettingsManager">
|
||||
<option name="PER_PROJECT_SETTINGS">
|
||||
<value>
|
||||
<option name="AUTODETECT_INDENTS" value="false" />
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
||||
<option name="JD_ADD_BLANK_AFTER_PARM_COMMENTS" value="true" />
|
||||
<option name="JD_ADD_BLANK_AFTER_RETURN" value="true" />
|
||||
<XML>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PROTECTED>true</PROTECTED>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PACKAGE_PRIVATE>true</PACKAGE_PRIVATE>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PRIVATE>true</PRIVATE>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PROTECTED>true</PROTECTED>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PACKAGE_PRIVATE>true</PACKAGE_PRIVATE>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PRIVATE>true</PRIVATE>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<INITIALIZER_BLOCK>true</INITIALIZER_BLOCK>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PROTECTED>true</PROTECTED>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PACKAGE_PRIVATE>true</PACKAGE_PRIVATE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PRIVATE>true</PRIVATE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PROTECTED>true</PROTECTED>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PACKAGE_PRIVATE>true</PACKAGE_PRIVATE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PRIVATE>true</PRIVATE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<FIELD>true</FIELD>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<INITIALIZER_BLOCK>true</INITIALIZER_BLOCK>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<CONSTRUCTOR>true</CONSTRUCTOR>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD>true</METHOD>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<METHOD>true</METHOD>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD>true</METHOD>
|
||||
<PRIVATE>true</PRIVATE>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<ENUM>true</ENUM>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<INTERFACE>true</INTERFACE>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<CLASS>true</CLASS>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<CLASS>true</CLASS>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
</value>
|
||||
</option>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
35
.travis.yml
Normal file
35
.travis.yml
Normal file
@ -0,0 +1,35 @@
|
||||
dist: focal
|
||||
|
||||
language: java
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- env:
|
||||
- JDK_VERSION=8
|
||||
- env:
|
||||
- JDK_VERSION=11
|
||||
- env:
|
||||
- JDK_VERSION=17
|
||||
|
||||
before_install:
|
||||
- "[[ -d $HOME/.sdkman/ ]] && [[ -d $HOME/.sdkman/bin/ ]] || rm -rf $HOME/.sdkman/"
|
||||
- curl -s "https://get.sdkman.io" | bash
|
||||
- mkdir -p "$HOME/.sdkman/etc/"
|
||||
- echo sdkman_auto_answer=true > "$HOME/.sdkman/etc/config"
|
||||
- echo sdkman_auto_selfupdate=true >> "$HOME/.sdkman/etc/config"
|
||||
- source "$HOME/.sdkman/bin/sdkman-init.sh"
|
||||
|
||||
install:
|
||||
- sdk install java $(sdk list java | grep -o "$JDK_VERSION\.[0-9]*\.[0-9]*\-open" | head -1)
|
||||
- sdk install maven
|
||||
- export JAVA_HOME="$HOME/.sdkman/candidates/java/current"
|
||||
- export PATH=${JAVA_HOME}/bin:${PATH}
|
||||
- export MAVEN_HOME="$HOME/.sdkman/candidates/maven/current"
|
||||
- export M2_HOME="$MAVEN_HOME"
|
||||
- export PATH=${M2_HOME}/bin:${PATH}
|
||||
- env
|
||||
- mvn -v
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2/repository
|
||||
674
LICENSE
Normal file
674
LICENSE
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{one line to give the program's name and a brief idea of what it does.}
|
||||
Copyright (C) {year} {name of author}
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
{project} Copyright (C) {year} {fullname}
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
4
README.md
Normal file
4
README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# AuthMeReReloaded
|
||||
**"A fork of the best authentication plugin for the Bukkit modding API!"**
|
||||
|
||||

|
||||
111
docs/commands.md
Normal file
111
docs/commands.md
Normal file
@ -0,0 +1,111 @@
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
<!-- 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 `< >`
|
||||
brackets; optional arguments are enclosed in square brackets (`[ ]`).
|
||||
|
||||
- **/authme**: The main AuthMeReloaded command. The root for all admin commands.
|
||||
- **/authme register** <player> <password>: Register the specified player with the specified password.
|
||||
<br />Requires `authme.admin.register`
|
||||
- **/authme unregister** <player>: Unregister the specified player.
|
||||
<br />Requires `authme.admin.unregister`
|
||||
- **/authme forcelogin** [player]: Enforce the specified player to login.
|
||||
<br />Requires `authme.admin.forcelogin`
|
||||
- **/authme password** <player> <pwd>: Change the password of a player.
|
||||
<br />Requires `authme.admin.changepassword`
|
||||
- **/authme lastlogin** [player]: View the date of the specified players last login.
|
||||
<br />Requires `authme.admin.lastlogin`
|
||||
- **/authme accounts** [player]: Display all accounts of a player by his player name or IP.
|
||||
<br />Requires `authme.admin.accounts`
|
||||
- **/authme email** [player]: Display the email address of the specified player if set.
|
||||
<br />Requires `authme.admin.getemail`
|
||||
- **/authme setemail** <player> <email>: Change the email address of the specified player.
|
||||
<br />Requires `authme.admin.changemail`
|
||||
- **/authme getip** <player>: Get the IP address of the specified online player.
|
||||
<br />Requires `authme.admin.getip`
|
||||
- **/authme totp** <player>: Returns whether the specified player has enabled two-factor authentication.
|
||||
<br />Requires `authme.admin.totpviewstatus`
|
||||
- **/authme disabletotp** <player>: Disable two-factor authentication for a player.
|
||||
<br />Requires `authme.admin.totpdisable`
|
||||
- **/authme spawn**: Teleport to the spawn.
|
||||
<br />Requires `authme.admin.spawn`
|
||||
- **/authme setspawn**: Change the player's spawn to your current position.
|
||||
<br />Requires `authme.admin.setspawn`
|
||||
- **/authme firstspawn**: Teleport to the first spawn.
|
||||
<br />Requires `authme.admin.firstspawn`
|
||||
- **/authme setfirstspawn**: Change the first player's spawn to your current position.
|
||||
<br />Requires `authme.admin.setfirstspawn`
|
||||
- **/authme purge** <days>: Purge old AuthMeReloaded data longer than the specified number of days ago.
|
||||
<br />Requires `authme.admin.purge`
|
||||
- **/authme purgeplayer** <player> [options]: Purges data of the given player.
|
||||
<br />Requires `authme.admin.purgeplayer`
|
||||
- **/authme backup**: Creates a backup of the registered users.
|
||||
<br />Requires `authme.admin.backup`
|
||||
- **/authme resetpos** <player/*>: Purge the last know position of the specified player or all of them.
|
||||
<br />Requires `authme.admin.purgelastpos`
|
||||
- **/authme purgebannedplayers**: Purge all AuthMeReloaded data for banned players.
|
||||
<br />Requires `authme.admin.purgebannedplayers`
|
||||
- **/authme switchantibot** [mode]: Switch or toggle the AntiBot mode to the specified state.
|
||||
<br />Requires `authme.admin.switchantibot`
|
||||
- **/authme reload**: Reload the AuthMeReloaded plugin.
|
||||
<br />Requires `authme.admin.reload`
|
||||
- **/authme version**: Show detailed information about the installed AuthMeReloaded version, the developers, contributors, and license.
|
||||
- **/authme converter** [job]: Converter command for AuthMeReloaded.
|
||||
<br />Requires `authme.admin.converter`
|
||||
- **/authme messages**: Adds missing texts to the current help messages file.
|
||||
<br />Requires `authme.admin.updatemessages`
|
||||
- **/authme recent**: Shows the last players that have logged in.
|
||||
<br />Requires `authme.admin.seerecent`
|
||||
- **/authme debug** [child] [arg] [arg]: Allows various operations for debugging.
|
||||
<br />Requires `authme.debug.command`
|
||||
- **/authme help** [query]: View detailed help for /authme commands.
|
||||
- **/email**: The AuthMeReloaded email command base.
|
||||
- **/email show**: Show your current email address.
|
||||
<br />Requires `authme.player.email.see`
|
||||
- **/email add** <email> <verifyEmail>: Add a new email address to your account.
|
||||
<br />Requires `authme.player.email.add`
|
||||
- **/email change** <oldEmail> <newEmail>: Change an email address of your account.
|
||||
<br />Requires `authme.player.email.change`
|
||||
- **/email recover** <email>: Recover your account using an Email address by sending a mail containing a new password.
|
||||
<br />Requires `authme.player.email.recover`
|
||||
- **/email code** <code>: Recover your account by submitting a code delivered to your email.
|
||||
<br />Requires `authme.player.email.recover`
|
||||
- **/email setpassword** <password>: Set a new password after successfully recovering your account.
|
||||
<br />Requires `authme.player.email.recover`
|
||||
- **/email help** [query]: View detailed help for /email commands.
|
||||
- **/login** <password>: Command to log in using AuthMeReloaded.
|
||||
<br />Requires `authme.player.login`
|
||||
- **/login help** [query]: View detailed help for /login commands.
|
||||
- **/logout**: Command to logout using AuthMeReloaded.
|
||||
<br />Requires `authme.player.logout`
|
||||
- **/logout help** [query]: View detailed help for /logout commands.
|
||||
- **/register** [password] [verifyPassword]: Command to register using AuthMeReloaded.
|
||||
<br />Requires `authme.player.register`
|
||||
- **/register help** [query]: View detailed help for /register commands.
|
||||
- **/unregister** <password>: Command to unregister using AuthMeReloaded.
|
||||
<br />Requires `authme.player.unregister`
|
||||
- **/unregister help** [query]: View detailed help for /unregister commands.
|
||||
- **/changepassword** <oldPassword> <newPassword>: Command to change your password using AuthMeReloaded.
|
||||
<br />Requires `authme.player.changepassword`
|
||||
- **/changepassword help** [query]: View detailed help for /changepassword commands.
|
||||
- **/totp**: Performs actions related to two-factor authentication.
|
||||
- **/totp code** <code>: Processes the two-factor authentication code during login.
|
||||
- **/totp add**: Enables two-factor authentication for your account.
|
||||
<br />Requires `authme.player.totpadd`
|
||||
- **/totp confirm** <code>: Saves the generated TOTP secret after confirmation.
|
||||
<br />Requires `authme.player.totpadd`
|
||||
- **/totp remove** <code>: Disables two-factor authentication for your account.
|
||||
<br />Requires `authme.player.totpremove`
|
||||
- **/totp help** [query]: View detailed help for /totp commands.
|
||||
- **/captcha** <captcha>: Captcha command for AuthMeReloaded.
|
||||
<br />Requires `authme.player.captcha`
|
||||
- **/captcha help** [query]: View detailed help for /captcha commands.
|
||||
- **/verification** <code>: Command to complete the verification process for AuthMeReloaded.
|
||||
<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 Sun Apr 04 21:31:42 CEST 2021
|
||||
584
docs/config.md
Normal file
584
docs/config.md
Normal file
@ -0,0 +1,584 @@
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
<!-- File auto-generated on Thu Jul 28 18:11:22 CEST 2022. 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,
|
||||
with which you can configure various settings. The following is the initial contents of
|
||||
the generated config.yml file.
|
||||
|
||||
```yml
|
||||
DataSource:
|
||||
# What type of database do you want to use?
|
||||
# Valid values: SQLITE, MARIADB, MYSQL, POSTGRESQL
|
||||
backend: SQLITE
|
||||
# Enable the database caching system, should be disabled on bungeecord environments
|
||||
# or when a website integration is being used.
|
||||
caching: true
|
||||
# Database host address
|
||||
mySQLHost: 127.0.0.1
|
||||
# Database port
|
||||
mySQLPort: '3306'
|
||||
# Connect to MySQL database over SSL
|
||||
mySQLUseSSL: true
|
||||
# Verification of server's certificate.
|
||||
# We would not recommend to set this option to false.
|
||||
# Set this option to false at your own risk if and only if you know what you're doing
|
||||
mySQLCheckServerCertificate: true
|
||||
# Authorize client to retrieve RSA server public key.
|
||||
# Advanced option, ignore if you don't know what it means.
|
||||
mySQLAllowPublicKeyRetrieval: true
|
||||
# Username to connect to the MySQL database
|
||||
mySQLUsername: authme
|
||||
# Password to connect to the MySQL database
|
||||
mySQLPassword: '12345'
|
||||
# Database Name, use with converters or as SQLITE database name
|
||||
mySQLDatabase: authme
|
||||
# Table of the database
|
||||
mySQLTablename: authme
|
||||
# Column of IDs to sort data
|
||||
mySQLColumnId: id
|
||||
# Column for storing or checking players nickname
|
||||
mySQLColumnName: username
|
||||
# Column for storing or checking players RealName
|
||||
mySQLRealName: realname
|
||||
# Column for storing players passwords
|
||||
mySQLColumnPassword: password
|
||||
# Column for storing players passwords salts
|
||||
mySQLColumnSalt: ''
|
||||
# Column for storing players emails
|
||||
mySQLColumnEmail: email
|
||||
# Column for storing if a player is logged in or not
|
||||
mySQLColumnLogged: isLogged
|
||||
# Column for storing if a player has a valid session or not
|
||||
mySQLColumnHasSession: hasSession
|
||||
# Column for storing a player's TOTP key (for two-factor authentication)
|
||||
mySQLtotpKey: totp
|
||||
# Column for storing the player's last IP
|
||||
mySQLColumnIp: ip
|
||||
# Column for storing players lastlogins
|
||||
mySQLColumnLastLogin: lastlogin
|
||||
# Column storing the registration date
|
||||
mySQLColumnRegisterDate: regdate
|
||||
# Column for storing the IP address at the time of registration
|
||||
mySQLColumnRegisterIp: regip
|
||||
# Column for storing player LastLocation - X
|
||||
mySQLlastlocX: x
|
||||
# Column for storing player LastLocation - Y
|
||||
mySQLlastlocY: y
|
||||
# Column for storing player LastLocation - Z
|
||||
mySQLlastlocZ: z
|
||||
# Column for storing player LastLocation - World Name
|
||||
mySQLlastlocWorld: world
|
||||
# Column for storing player LastLocation - Yaw
|
||||
mySQLlastlocYaw: yaw
|
||||
# Column for storing player LastLocation - Pitch
|
||||
mySQLlastlocPitch: pitch
|
||||
# Column for storing players uuids (optional)
|
||||
mySQLPlayerUUID: ''
|
||||
# Overrides the size of the DB Connection Pool, default = 10
|
||||
poolSize: 10
|
||||
# The maximum lifetime of a connection in the pool, default = 1800 seconds
|
||||
# You should set this at least 30 seconds less than mysql server wait_timeout
|
||||
maxLifetime: 1800
|
||||
ExternalBoardOptions:
|
||||
# Column for storing players groups
|
||||
mySQLColumnGroup: ''
|
||||
# -1 means disabled. If you want that only activated players
|
||||
# can log into your server, you can set here the group number
|
||||
# of unactivated users, needed for some forum/CMS support
|
||||
nonActivedUserGroup: -1
|
||||
# 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: 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
|
||||
phpbbActivatedGroupId: 2
|
||||
# IP Board table prefix defined during the IP Board installation process
|
||||
IPBTablePrefix: ipb_
|
||||
# IP Board default group ID; 3 is the default registered group defined by IP Board
|
||||
IPBActivatedGroupId: 3
|
||||
# Xenforo table prefix defined during the Xenforo installation process
|
||||
XFTablePrefix: xf_
|
||||
# XenForo default group ID; 2 is the default registered group defined by Xenforo
|
||||
XFActivatedGroupId: 2
|
||||
# Wordpress prefix defined during WordPress installation
|
||||
wordpressTablePrefix: wp_
|
||||
settings:
|
||||
sessions:
|
||||
# Do you want to enable the session feature?
|
||||
# If enabled, when a player authenticates successfully,
|
||||
# his IP and his nickname is saved.
|
||||
# The next time the player joins the server, if his IP
|
||||
# is the same as last time and the timeout hasn't
|
||||
# expired, he will not need to authenticate.
|
||||
enabled: false
|
||||
# After how many minutes should a session expire?
|
||||
# A player's session ends after the timeout or if his IP has changed
|
||||
timeout: 10
|
||||
# Message language, available languages:
|
||||
# https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/translations.md
|
||||
messagesLanguage: en
|
||||
# Forces authme to hook into Vault instead of a specific permission handler system.
|
||||
forceVaultHook: false
|
||||
# Log level: INFO, FINE, DEBUG. Use INFO for general messages,
|
||||
# FINE for some additional detailed ones (like password failed),
|
||||
# and DEBUG for debugging
|
||||
logLevel: FINE
|
||||
# By default we schedule async tasks when talking to the database. If you want
|
||||
# typical communication with the database to happen synchronously, set this to false
|
||||
useAsyncTasks: true
|
||||
# The name of the server, used in some placeholders.
|
||||
serverName: Your Minecraft Server
|
||||
restrictions:
|
||||
# Can not authenticated players chat?
|
||||
# Keep in mind that this feature also blocks all commands not
|
||||
# listed in the list below.
|
||||
allowChat: false
|
||||
# Hide the chat log from players who are not authenticated?
|
||||
hideChat: false
|
||||
# Allowed commands for unauthenticated players
|
||||
allowCommands:
|
||||
- /login
|
||||
- /log
|
||||
- /l
|
||||
- /register
|
||||
- /reg
|
||||
- /email
|
||||
- /captcha
|
||||
- /2fa
|
||||
- /totp
|
||||
# Max number of allowed registrations per IP
|
||||
# The value 0 means an unlimited number of registrations!
|
||||
maxRegPerIp: 1
|
||||
# Minimum allowed username length
|
||||
minNicknameLength: 3
|
||||
# Maximum allowed username length
|
||||
maxNicknameLength: 16
|
||||
# When this setting is enabled, online players can't be kicked out
|
||||
# due to "Logged in from another Location"
|
||||
# This setting will prevent potential security exploits.
|
||||
ForceSingleSession: true
|
||||
ForceSpawnLocOnJoin:
|
||||
# If enabled, every player that spawn in one of the world listed in
|
||||
# "ForceSpawnLocOnJoin.worlds" will be teleported to the spawnpoint after successful
|
||||
# authentication. The quit location of the player will be overwritten.
|
||||
# This is different from "teleportUnAuthedToSpawn" that teleport player
|
||||
# to the spawnpoint on join.
|
||||
enabled: false
|
||||
# WorldNames where we need to force the spawn location
|
||||
# Case-sensitive!
|
||||
worlds:
|
||||
- world
|
||||
- world_nether
|
||||
- world_the_end
|
||||
# This option will save the quit location of the players.
|
||||
SaveQuitLocation: false
|
||||
# To activate the restricted user feature you need
|
||||
# to enable this option and configure the AllowedRestrictedUser field.
|
||||
AllowRestrictedUser: false
|
||||
# The restricted user feature will kick players listed below
|
||||
# if they don't match the defined IP address. Names are case-insensitive.
|
||||
# You can use * as wildcard (127.0.0.*), or regex with a "regex:" prefix regex:127\.0\.0\..*
|
||||
# Example:
|
||||
# AllowedRestrictedUser:
|
||||
# - playername;127.0.0.1
|
||||
# - playername;regex:127\.0\.0\..*
|
||||
AllowedRestrictedUser: []
|
||||
# Ban unknown IPs trying to log in with a restricted username?
|
||||
banUnsafedIP: false
|
||||
# Should unregistered players be kicked immediately?
|
||||
kickNonRegistered: false
|
||||
# Should players be kicked on wrong password?
|
||||
kickOnWrongPassword: true
|
||||
# Should not logged in players be teleported to the spawn?
|
||||
# After the authentication they will be teleported back to
|
||||
# their normal position.
|
||||
teleportUnAuthedToSpawn: false
|
||||
# Can unregistered players walk around?
|
||||
allowMovement: false
|
||||
# After how many seconds should players who fail to login or register
|
||||
# be kicked? Set to 0 to disable.
|
||||
timeout: 30
|
||||
# Regex pattern of allowed characters in the player name.
|
||||
allowedNicknameCharacters: '[a-zA-Z0-9_]*'
|
||||
# How far can unregistered players walk?
|
||||
# Set to 0 for unlimited radius
|
||||
allowedMovementRadius: 100
|
||||
# Should we protect the player inventory before logging in? Requires ProtocolLib.
|
||||
ProtectInventoryBeforeLogIn: true
|
||||
# Should we deny the tabcomplete feature before logging in? Requires ProtocolLib.
|
||||
DenyTabCompleteBeforeLogin: false
|
||||
# Should we display all other accounts from a player when he joins?
|
||||
# permission: /authme.admin.accounts
|
||||
displayOtherAccounts: true
|
||||
# Spawn priority; values: authme, essentials, cmi, multiverse, default
|
||||
spawnPriority: authme,essentials,cmi,multiverse,default
|
||||
# Maximum Login authorized by IP
|
||||
maxLoginPerIp: 0
|
||||
# Maximum Join authorized by IP
|
||||
maxJoinPerIp: 0
|
||||
# AuthMe will NEVER teleport players if set to true!
|
||||
noTeleport: false
|
||||
# Regex syntax for allowed chars in passwords. The default [!-~] allows all visible ASCII
|
||||
# characters, which is what we recommend. See also http://asciitable.com
|
||||
# You can test your regex with https://regex101.com
|
||||
allowedPasswordCharacters: '[!-~]*'
|
||||
GameMode:
|
||||
# Force survival gamemode when player joins?
|
||||
ForceSurvivalMode: false
|
||||
unrestrictions:
|
||||
# Below you can list all account names that AuthMe will ignore
|
||||
# for registration or login. Configure it at your own risk!!
|
||||
# This option adds compatibility with BuildCraft and some other mods.
|
||||
# It is case-insensitive! Example:
|
||||
# UnrestrictedName:
|
||||
# - 'npcPlayer'
|
||||
# - 'npcPlayer2'
|
||||
UnrestrictedName: []
|
||||
# Below you can list all inventories names that AuthMe will ignore
|
||||
# for registration or login. Configure it at your own risk!!
|
||||
# This option adds compatibility with some mods.
|
||||
# It is case-insensitive! Example:
|
||||
# UnrestrictedInventories:
|
||||
# - 'myCustomInventory1'
|
||||
# - 'myCustomInventory2'
|
||||
UnrestrictedInventories: []
|
||||
security:
|
||||
# Minimum length of password
|
||||
minPasswordLength: 5
|
||||
# Maximum length of password
|
||||
passwordMaxLength: 30
|
||||
# Possible values: SHA256, BCRYPT, BCRYPT2Y, PBKDF2, SALTEDSHA512,
|
||||
# MYBB, IPB3, PHPBB, PHPFUSION, SMF, XENFORO, XAUTH, JOOMLA, WBB3, WBB4, MD5VB,
|
||||
# PBKDF2DJANGO, WORDPRESS, ROYALAUTH, ARGON2, CUSTOM (for developers only). See full list at
|
||||
# https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/hash_algorithms.md
|
||||
# If you use ARGON2, check that you have the argon2 c library on your system
|
||||
passwordHash: SHA256
|
||||
# If a password check fails, AuthMe will also try to check with the following hash methods.
|
||||
# Use this setting when you change from one hash method to another.
|
||||
# AuthMe will update the password to the new hash. Example:
|
||||
# legacyHashes:
|
||||
# - 'SHA1'
|
||||
legacyHashes: []
|
||||
# Salt length for the SALTED2MD5 MD5(MD5(password)+salt)
|
||||
doubleMD5SaltLength: 8
|
||||
# Number of rounds to use if passwordHash is set to PBKDF2. Default is 10000
|
||||
pbkdf2Rounds: 10000
|
||||
# Prevent unsafe passwords from being used; put them in lowercase!
|
||||
# You should always set 'help' as unsafePassword due to possible conflicts.
|
||||
# unsafePasswords:
|
||||
# - '123456'
|
||||
# - 'password'
|
||||
# - 'help'
|
||||
unsafePasswords:
|
||||
- '123456'
|
||||
- password
|
||||
- qwerty
|
||||
- '12345'
|
||||
- '54321'
|
||||
- '123456789'
|
||||
- help
|
||||
registration:
|
||||
# Enable registration on the server?
|
||||
enabled: true
|
||||
# Send every X seconds a message to a player to
|
||||
# remind him that he has to login/register
|
||||
messageInterval: 5
|
||||
# Only registered and logged in players can play.
|
||||
# See restrictions for exceptions
|
||||
force: true
|
||||
# Type of registration: PASSWORD or EMAIL
|
||||
# PASSWORD = account is registered with a password supplied by the user;
|
||||
# EMAIL = password is generated and sent to the email provided by the user.
|
||||
# More info at https://github.com/AuthMe/AuthMeReloaded/wiki/Registration
|
||||
type: PASSWORD
|
||||
# Second argument the /register command should take: NONE = no 2nd argument
|
||||
# CONFIRMATION = must repeat first argument (pass or email)
|
||||
# EMAIL_OPTIONAL = for password register: 2nd argument can be empty or have email address
|
||||
# EMAIL_MANDATORY = for password register: 2nd argument MUST be an email address
|
||||
secondArg: CONFIRMATION
|
||||
# Do we force kick a player after a successful registration?
|
||||
# Do not use with login feature below
|
||||
forceKickAfterRegister: false
|
||||
# Does AuthMe need to enforce a /login after a successful registration?
|
||||
forceLoginAfterRegister: false
|
||||
# Broadcast the welcome message to the server or only to the player?
|
||||
# set true for server or false for player
|
||||
broadcastWelcomeMessage: false
|
||||
# Should we delay the join message and display it once the player has logged in?
|
||||
delayJoinMessage: false
|
||||
# The custom join message that will be sent after a successful login,
|
||||
# keep empty to use the original one.
|
||||
# Available variables:
|
||||
# {PLAYERNAME}: the player name (no colors)
|
||||
# {DISPLAYNAME}: the player display name (with colors)
|
||||
# {DISPLAYNAMENOCOLOR}: the player display name (without colors)
|
||||
customJoinMessage: ''
|
||||
# Should we remove the leave messages of unlogged users?
|
||||
removeUnloggedLeaveMessage: false
|
||||
# Should we remove join messages altogether?
|
||||
removeJoinMessage: false
|
||||
# Should we remove leave messages altogether?
|
||||
removeLeaveMessage: false
|
||||
# Do we need to add potion effect Blinding before login/register?
|
||||
applyBlindEffect: false
|
||||
# Do we need to prevent people to login with another case?
|
||||
# If Xephi is registered, then Xephi can login, but not XEPHI/xephi/XePhI
|
||||
preventOtherCase: true
|
||||
GroupOptions:
|
||||
# Enables switching a player to defined permission groups before they log in.
|
||||
# See below for a detailed explanation.
|
||||
enablePermissionCheck: false
|
||||
# This is a very important option: if a registered player joins the server
|
||||
# AuthMe will switch him to unLoggedInGroup. This should prevent all major exploits.
|
||||
# You can set up your permission plugin with this special group to have no permissions,
|
||||
# or only permission to chat (or permission to send private messages etc.).
|
||||
# The better way is to set up this group with few permissions, so if a player
|
||||
# tries to exploit an account they can do only what you've defined for the group.
|
||||
# After login, the player will be moved to his correct permissions group!
|
||||
# Please note that the group name is case-sensitive, so 'admin' is different from 'Admin'
|
||||
# Otherwise your group will be wiped and the player will join in the default group []!
|
||||
# Example: registeredPlayerGroup: 'NotLogged'
|
||||
registeredPlayerGroup: ''
|
||||
# Similar to above, unregistered players can be set to the following
|
||||
# permissions group
|
||||
unregisteredPlayerGroup: ''
|
||||
Email:
|
||||
# Email SMTP server host
|
||||
mailSMTP: smtp.gmail.com
|
||||
# Email SMTP server port
|
||||
mailPort: 465
|
||||
# Only affects port 25: enable TLS/STARTTLS?
|
||||
useTls: true
|
||||
# Email account which sends the mails
|
||||
mailAccount: ''
|
||||
# Email account password
|
||||
mailPassword: ''
|
||||
# Email address, fill when mailAccount is not the email address of the account
|
||||
mailAddress: ''
|
||||
# Custom sender name, replacing the mailAccount name in the email
|
||||
mailSenderName: ''
|
||||
# Recovery password length
|
||||
RecoveryPasswordLength: 8
|
||||
# Mail Subject
|
||||
mailSubject: Your new AuthMe password
|
||||
# Like maxRegPerIP but with email
|
||||
maxRegPerEmail: 1
|
||||
# Recall players to add an email?
|
||||
recallPlayers: false
|
||||
# Delay in minute for the recall scheduler
|
||||
delayRecall: 5
|
||||
# Blacklist these domains for emails
|
||||
emailBlacklisted:
|
||||
- 10minutemail.com
|
||||
# Whitelist ONLY these domains for emails
|
||||
emailWhitelisted: []
|
||||
# Send the new password drawn in an image?
|
||||
generateImage: false
|
||||
# The OAuth2 token
|
||||
emailOauth2Token: ''
|
||||
Hooks:
|
||||
# Do we need to hook with multiverse for spawn checking?
|
||||
multiverse: true
|
||||
# Do we need to hook with BungeeCord?
|
||||
bungeecord: false
|
||||
# Send player to this BungeeCord server after register/login
|
||||
sendPlayerTo: ''
|
||||
# Do we need to disable Essentials SocialSpy on join?
|
||||
disableSocialSpy: false
|
||||
# Do we need to force /motd Essentials command on join?
|
||||
useEssentialsMotd: false
|
||||
Protection:
|
||||
# Enable some servers protection (country based login, antibot)
|
||||
enableProtection: false
|
||||
# Apply the protection also to registered usernames
|
||||
enableProtectionRegistered: true
|
||||
geoIpDatabase:
|
||||
# The MaxMind clientId used to download the GeoIp database,
|
||||
# get one at https://www.maxmind.com/en/accounts/current/license-key
|
||||
# The EssentialsX project has a very useful tutorial on how to generate
|
||||
# the license key: https://github.com/EssentialsX/Wiki/blob/master/GeoIP.md
|
||||
clientId: ''
|
||||
# The MaxMind licenseKey used to download the GeoIp database.
|
||||
licenseKey: ''
|
||||
# Countries allowed to join the server and register. For country codes, see
|
||||
# https://dev.maxmind.com/geoip/legacy/codes/iso3166/
|
||||
# Use "LOCALHOST" for local addresses.
|
||||
# PLEASE USE QUOTES!
|
||||
countries:
|
||||
- US
|
||||
- GB
|
||||
- LOCALHOST
|
||||
# Countries not allowed to join the server and register
|
||||
# PLEASE USE QUOTES!
|
||||
countriesBlacklist:
|
||||
- A1
|
||||
# Do we need to enable automatic antibot system?
|
||||
enableAntiBot: true
|
||||
# The interval in seconds
|
||||
antiBotInterval: 5
|
||||
# Max number of players allowed to login in the interval
|
||||
# before the AntiBot system is enabled automatically
|
||||
antiBotSensibility: 10
|
||||
# Duration in minutes of the antibot automatic system
|
||||
antiBotDuration: 10
|
||||
# Delay in seconds before the antibot activation
|
||||
antiBotDelay: 60
|
||||
quickCommands:
|
||||
# Kicks the player that issued a command before the defined time after the join process
|
||||
denyCommandsBeforeMilliseconds: 1000
|
||||
Purge:
|
||||
# If enabled, AuthMe automatically purges old, unused accounts
|
||||
useAutoPurge: false
|
||||
# Number of days after which an account should be purged
|
||||
daysBeforeRemovePlayer: 60
|
||||
# Do we need to remove the player.dat file during purge process?
|
||||
removePlayerDat: false
|
||||
# Do we need to remove the Essentials/userdata/player.yml file during purge process?
|
||||
removeEssentialsFile: false
|
||||
# World in which the players.dat are stored
|
||||
defaultWorld: world
|
||||
# Remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge?
|
||||
removeLimitedCreativesInventories: false
|
||||
# Do we need to remove the AntiXRayData/PlayerData/player file during purge process?
|
||||
removeAntiXRayFile: false
|
||||
# Do we need to remove permissions?
|
||||
removePermissions: false
|
||||
Security:
|
||||
SQLProblem:
|
||||
# Stop the server if we can't contact the sql database
|
||||
# Take care with this, if you set this to false,
|
||||
# AuthMe will automatically disable and the server won't be protected!
|
||||
stopServer: true
|
||||
console:
|
||||
# Copy AuthMe log output in a separate file as well?
|
||||
logConsole: true
|
||||
captcha:
|
||||
# Enable captcha when a player uses wrong password too many times
|
||||
useCaptcha: false
|
||||
# Max allowed tries before a captcha is required
|
||||
maxLoginTry: 5
|
||||
# Captcha length
|
||||
captchaLength: 5
|
||||
# Minutes after which login attempts count is reset for a player
|
||||
captchaCountReset: 60
|
||||
# Require captcha before a player may register?
|
||||
requireForRegistration: false
|
||||
tempban:
|
||||
# Tempban a user's IP address if they enter the wrong password too many times
|
||||
enableTempban: false
|
||||
# How many times a user can attempt to login before their IP being tempbanned
|
||||
maxLoginTries: 10
|
||||
# The length of time a IP address will be tempbanned in minutes
|
||||
# Default: 480 minutes, or 8 hours
|
||||
tempbanLength: 480
|
||||
# How many minutes before resetting the count for failed logins by IP and username
|
||||
# Default: 480 minutes (8 hours)
|
||||
minutesBeforeCounterReset: 480
|
||||
# The command to execute instead of using the internal ban system, empty if disabled.
|
||||
# Available placeholders: %player%, %ip%
|
||||
customCommand: ''
|
||||
recoveryCode:
|
||||
# Number of characters a recovery code should have (0 to disable)
|
||||
length: 8
|
||||
# How many hours is a recovery code valid for?
|
||||
validForHours: 4
|
||||
# Max number of tries to enter recovery code
|
||||
maxTries: 3
|
||||
# How long a player has after password recovery to change their password
|
||||
# without logging in. This is in minutes.
|
||||
# Default: 2 minutes
|
||||
passwordChangeTimeout: 2
|
||||
emailRecovery:
|
||||
# Seconds a user has to wait for before a password recovery mail may be sent again
|
||||
# This prevents an attacker from abusing AuthMe's email feature.
|
||||
cooldown: 60
|
||||
privacy:
|
||||
# The mail shown using /email show will be partially hidden
|
||||
# E.g. (if enabled)
|
||||
# original email: my.email@example.com
|
||||
# hidden email: my.***@***mple.com
|
||||
enableEmailMasking: false
|
||||
# Minutes after which a verification code will expire
|
||||
verificationCodeExpiration: 10
|
||||
# Before a user logs in, various properties are temporarily removed from the player,
|
||||
# such as OP status, ability to fly, and walk/fly speed.
|
||||
# Once the user is logged in, we add back the properties we previously saved.
|
||||
# In this section, you may define how these properties should be handled.
|
||||
# Read more at https://github.com/AuthMe/AuthMeReloaded/wiki/Limbo-players
|
||||
limbo:
|
||||
persistence:
|
||||
# Besides storing the data in memory, you can define if/how the data should be persisted
|
||||
# on disk. This is useful in case of a server crash, so next time the server starts we can
|
||||
# properly restore things like OP status, ability to fly, and walk/fly speed.
|
||||
# DISABLED: no disk storage,
|
||||
# INDIVIDUAL_FILES: each player data in its own file,
|
||||
# DISTRIBUTED_FILES: distributes players into different files based on their UUID, see below
|
||||
type: INDIVIDUAL_FILES
|
||||
# This setting only affects DISTRIBUTED_FILES persistence. The distributed file
|
||||
# persistence attempts to reduce the number of files by distributing players into various
|
||||
# buckets based on their UUID. This setting defines into how many files the players should
|
||||
# be distributed. Possible values: ONE, FOUR, EIGHT, SIXTEEN, THIRTY_TWO, SIXTY_FOUR,
|
||||
# ONE_TWENTY for 128, TWO_FIFTY for 256.
|
||||
# For example, if you expect 100 non-logged in players, setting to SIXTEEN will average
|
||||
# 6.25 players per file (100 / 16).
|
||||
# Note: if you change this setting all data will be migrated. If you have a lot of data,
|
||||
# change this setting only on server restart, not with /authme reload.
|
||||
distributionSize: SIXTEEN
|
||||
# Whether the player is allowed to fly: RESTORE, ENABLE, DISABLE, NOTHING.
|
||||
# RESTORE sets back the old property from the player. NOTHING will prevent AuthMe
|
||||
# from modifying the 'allow flight' property on the player.
|
||||
restoreAllowFlight: RESTORE
|
||||
# Restore fly speed: RESTORE, DEFAULT, MAX_RESTORE, RESTORE_NO_ZERO.
|
||||
# RESTORE: restore the speed the player had;
|
||||
# DEFAULT: always set to default speed;
|
||||
# MAX_RESTORE: take the maximum of the player's current speed and the previous one
|
||||
# RESTORE_NO_ZERO: Like 'restore' but sets speed to default if the player's speed was 0
|
||||
restoreFlySpeed: RESTORE_NO_ZERO
|
||||
# Restore walk speed: RESTORE, DEFAULT, MAX_RESTORE, RESTORE_NO_ZERO.
|
||||
# See above for a description of the values.
|
||||
restoreWalkSpeed: RESTORE_NO_ZERO
|
||||
BackupSystem:
|
||||
# General configuration for backups: if false, no backups are possible
|
||||
ActivateBackup: false
|
||||
# Create backup at every start of server
|
||||
OnServerStart: false
|
||||
# Create backup at every stop of server
|
||||
OnServerStop: true
|
||||
# Windows only: MySQL installation path
|
||||
MysqlWindowsPath: C:\Program Files\MySQL\MySQL Server 5.1\
|
||||
# Converter settings: see https://github.com/AuthMe/AuthMeReloaded/wiki/Converters
|
||||
Converter:
|
||||
Rakamak:
|
||||
# Rakamak file name
|
||||
fileName: users.rak
|
||||
# Rakamak use IP?
|
||||
useIP: false
|
||||
# Rakamak IP file name
|
||||
ipFileName: UsersIp.rak
|
||||
CrazyLogin:
|
||||
# CrazyLogin database file name
|
||||
fileName: accounts.db
|
||||
loginSecurity:
|
||||
# LoginSecurity: convert from SQLite; if false we use MySQL
|
||||
useSqlite: true
|
||||
mySql:
|
||||
# LoginSecurity MySQL: database host
|
||||
host: ''
|
||||
# LoginSecurity MySQL: database name
|
||||
database: ''
|
||||
# LoginSecurity MySQL: database user
|
||||
user: ''
|
||||
# LoginSecurity MySQL: password for database user
|
||||
password: ''
|
||||
|
||||
```
|
||||
|
||||
To change settings on a running server, save your changes to config.yml and use
|
||||
`/authme reload`.
|
||||
|
||||
---
|
||||
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Thu Jul 28 18:11:22 CEST 2022
|
||||
83
docs/hash_algorithms.md
Normal file
83
docs/hash_algorithms.md
Normal file
@ -0,0 +1,83 @@
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
<!-- 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.
|
||||
|
||||
|
||||
Algorithm | Recommendation | Hash length | ASCII | | Salt type | Length | Separate?
|
||||
--------- | -------------- | ----------- | ----- | --- | --------- | ------ | ---------
|
||||
ARGON2 | Recommended | 96 | | | Text | 16 |
|
||||
BCRYPT | Recommended | 60 | | | Text | 22 |
|
||||
BCRYPT2Y | Recommended | 60 | | | Text | 22 |
|
||||
CMW | Do not use | 32 | | | None | |
|
||||
CRAZYCRYPT1 | Do not use | 128 | | | Username | |
|
||||
IPB3 | Acceptable | 32 | | | Text | 5 | Y
|
||||
IPB4 | Does not work | 60 | | | Text | 22 | Y
|
||||
JOOMLA | Acceptable | 65 | | | Text | 32 |
|
||||
MD5VB | Acceptable | 56 | | | Text | 16 |
|
||||
MYBB | Acceptable | 32 | | | Text | 8 | Y
|
||||
PBKDF2 | Recommended | 165 | | | Text | 16 |
|
||||
PBKDF2DJANGO | Acceptable | 77 | Y | | Text | 12 |
|
||||
PHPBB | Acceptable | 60 | | | Text | 22 |
|
||||
PHPFUSION | Do not use | 64 | Y | | | | Y
|
||||
ROYALAUTH | Do not use | 128 | | | None | |
|
||||
SALTED2MD5 | Acceptable | 32 | | | Text | | Y
|
||||
SALTEDSHA512 | Recommended | 128 | | | | | Y
|
||||
SHA256 | Recommended | 86 | | | Text | 16 |
|
||||
SMF | Do not use | 40 | | | Username | | Y
|
||||
TWO_FACTOR | Does not work | 16 | | | None | |
|
||||
WBB3 | Acceptable | 40 | | | Text | 40 | Y
|
||||
WBB4 | Recommended | 60 | | | Text | 22 |
|
||||
WORDPRESS | Acceptable | 34 | | | Text | 9 |
|
||||
XAUTH | Recommended | 140 | | | Text | 12 |
|
||||
XFBCRYPT | Recommended | 60 | | | Text | 22 |
|
||||
CUSTOM | | | | | | | |
|
||||
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
|
||||
### Columns
|
||||
#### Algorithm
|
||||
The algorithm is the hashing algorithm used to store passwords with. Default is SHA256 and is recommended.
|
||||
You can change the hashing algorithm in the config.yml: under `security`, locate `passwordHash`.
|
||||
|
||||
#### Recommendation
|
||||
The recommendation lists our usage recommendation in terms of how secure it is (not how _well_ the algorithm works!).
|
||||
- Recommended: The hash algorithm appears to be cryptographically secure and is one we recommend.
|
||||
- Acceptable: There are safer algorithms that can be chosen but using the algorithm is generally OK.
|
||||
- Deprecated: Cannot be used anymore actively or in the near future.
|
||||
- Do not use: Hash algorithm isn't sufficiently secure. Use only if required to hook into another system.
|
||||
- Does not work: The algorithm does not work properly; do not use.
|
||||
|
||||
#### Hash Length
|
||||
The length of the hashes the algorithm produces. Note that the hash length is not (primarily) indicative of
|
||||
whether an algorithm is secure or not.
|
||||
|
||||
#### ASCII
|
||||
If denoted with a **y**, means that the algorithm is restricted to ASCII characters only, i.e. it will simply ignore
|
||||
"special characters" such as `ÿ` or `Â`. Note that we do not recommend the use of "special characters" in passwords.
|
||||
|
||||
#### Salt Columns
|
||||
Before hashing, a _salt_ may be appended to the password to make the hash more secure. The following columns describe
|
||||
the salt the algorithm uses.
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
|
||||
##### Salt Type
|
||||
We do not recommend the usage
|
||||
of any algorithm that doesn't use a randomly generated text as salt. This "salt type" column indicates what type of
|
||||
salt the algorithm uses:
|
||||
- Text: randomly generated text (see also the following column, "Length")
|
||||
- Username: the salt is constructed from the username (bad)
|
||||
- None: the algorithm uses no salt (bad)
|
||||
|
||||
##### Length
|
||||
If applicable (salt type is "Text"), indicates the length of the generated salt. The longer the better.
|
||||
If this column is empty when the salt type is "Text", it typically means the salt length can be defined in config.yml.
|
||||
|
||||
##### Separate
|
||||
If denoted with a **y**, it means that the salt is stored in a separate column in the database. This is neither good
|
||||
or bad.
|
||||
|
||||
---
|
||||
|
||||
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
|
||||
76
docs/permission_nodes.md
Normal file
76
docs/permission_nodes.md
Normal file
@ -0,0 +1,76 @@
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
<!-- 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.
|
||||
|
||||
- **authme.admin.*** – Give access to all admin commands.
|
||||
- **authme.admin.accounts** – Administrator command to see all accounts associated with a user.
|
||||
- **authme.admin.antibotmessages** – Permission to see Antibot messages.
|
||||
- **authme.admin.backup** – Allows to use the backup command.
|
||||
- **authme.admin.changemail** – Administrator command to set or change the email address of a user.
|
||||
- **authme.admin.changepassword** – Administrator command to change the password of a user.
|
||||
- **authme.admin.converter** – Administrator command to convert old or other data to AuthMe data.
|
||||
- **authme.admin.firstspawn** – Administrator command to teleport to the first AuthMe spawn.
|
||||
- **authme.admin.forcelogin** – Administrator command to force-login an existing user.
|
||||
- **authme.admin.getemail** – Administrator command to get the email address of a user, if set.
|
||||
- **authme.admin.getip** – Administrator command to get the last known IP of a user.
|
||||
- **authme.admin.lastlogin** – Administrator command to see the last login date and time of a user.
|
||||
- **authme.admin.purge** – Administrator command to purge old user data.
|
||||
- **authme.admin.purgebannedplayers** – Administrator command to purge all data associated with banned players.
|
||||
- **authme.admin.purgelastpos** – Administrator command to purge the last position of a user.
|
||||
- **authme.admin.purgeplayer** – Administrator command to purge a given player.
|
||||
- **authme.admin.register** – Administrator command to register a new user.
|
||||
- **authme.admin.reload** – Administrator command to reload the plugin configuration.
|
||||
- **authme.admin.seeotheraccounts** – Permission to see the other accounts of the players that log in.
|
||||
- **authme.admin.seerecent** – Administrator command to see the last recently logged in players.
|
||||
- **authme.admin.setfirstspawn** – Administrator command to set the first AuthMe spawn.
|
||||
- **authme.admin.setspawn** – Administrator command to set the AuthMe spawn.
|
||||
- **authme.admin.spawn** – Administrator command to teleport to the AuthMe spawn.
|
||||
- **authme.admin.switchantibot** – Administrator command to toggle the AntiBot protection status.
|
||||
- **authme.admin.totpdisable** – Administrator command to disable the two-factor auth of a user.
|
||||
- **authme.admin.totpviewstatus** – Administrator command to see whether a player has enabled two-factor authentication.
|
||||
- **authme.admin.unregister** – Administrator command to unregister an existing user.
|
||||
- **authme.admin.updatemessages** – Permission to use the update messages command.
|
||||
- **authme.allowchatbeforelogin** – Permission to send chat messages before being logged in.
|
||||
- **authme.allowmultipleaccounts** – Permission to be able to register multiple accounts.
|
||||
- **authme.bypassantibot** – Permission node to bypass AntiBot protection.
|
||||
- **authme.bypassbungeesend** – Permission node to bypass BungeeCord server teleportation.
|
||||
- **authme.bypasscountrycheck** – Permission to bypass the GeoIp country code check.
|
||||
- **authme.bypassforcesurvival** – Permission for users to bypass force-survival mode.
|
||||
- **authme.bypasspurge** – Permission to bypass the purging process.
|
||||
- **authme.debug.command** – General permission to use the /authme debug command.
|
||||
- **authme.debug.country** – Permission to use the country lookup section.
|
||||
- **authme.debug.db** – Permission to view data from the database.
|
||||
- **authme.debug.group** – Permission to view permission groups.
|
||||
- **authme.debug.limbo** – Permission to use the limbo data viewer.
|
||||
- **authme.debug.mail** – Permission to use the test email sender.
|
||||
- **authme.debug.mysqldef** – Permission to change nullable status of MySQL columns.
|
||||
- **authme.debug.perm** – Permission to use the permission checker.
|
||||
- **authme.debug.spawn** – Permission to view spawn information.
|
||||
- **authme.debug.stats** – Permission to use the stats section.
|
||||
- **authme.debug.valid** – Permission to use sample validation.
|
||||
- **authme.player.*** – Permission to use all player (non-admin) commands.
|
||||
- **authme.player.canbeforced** – Permission for users a login can be forced to.
|
||||
- **authme.player.captcha** – Command permission to use captcha.
|
||||
- **authme.player.changepassword** – Command permission to change the password.
|
||||
- **authme.player.email** – Grants all email permissions.
|
||||
- **authme.player.email.add** – Command permission to add an email address.
|
||||
- **authme.player.email.change** – Command permission to change the email address.
|
||||
- **authme.player.email.recover** – Command permission to recover an account using its email address.
|
||||
- **authme.player.email.see** – Command permission to see the own email address.
|
||||
- **authme.player.login** – Command permission to login.
|
||||
- **authme.player.logout** – Command permission to logout.
|
||||
- **authme.player.protection.quickcommandsprotection** – Permission that enables on join quick commands checks for the player.
|
||||
- **authme.player.register** – Command permission to register.
|
||||
- **authme.player.security.verificationcode** – Permission to use the email verification codes feature.
|
||||
- **authme.player.seeownaccounts** – Permission to use to see own other accounts.
|
||||
- **authme.player.totpadd** – Permission to enable two-factor authentication.
|
||||
- **authme.player.totpremove** – Permission to disable two-factor authentication.
|
||||
- **authme.player.unregister** – Command permission to unregister.
|
||||
- **authme.vip** – When the server is full and someone with this permission joins the server, someone will be kicked.
|
||||
|
||||
|
||||
---
|
||||
|
||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 04 21:31:44 CEST 2021
|
||||
46
docs/translations.md
Normal file
46
docs/translations.md
Normal file
@ -0,0 +1,46 @@
|
||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||
<!-- 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
|
||||
in your config.yml to use the language, or use another language code to start a new translation.
|
||||
|
||||
Code | Language | Translated |
|
||||
---- | -------- | ---------: | ------
|
||||
[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 | 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 | 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" />
|
||||
[hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
|
||||
[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 93% | <img src="https://via.placeholder.com/93x7/77dd44?text=%20" alt="93" />
|
||||
[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
|
||||
[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Lithuanian | 36% | <img src="https://via.placeholder.com/36x7/aa4400?text=%20" alt="36" />
|
||||
[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 | 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" />
|
||||
[sr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sr.yml) | Serbian | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
|
||||
[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 77% | <img src="https://via.placeholder.com/77x7/bb9900?text=%20" alt="77" />
|
||||
[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 100% | <img src="https://via.placeholder.com/100x7/66ff66?text=%20" alt="100" />
|
||||
[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 99% | <img src="https://via.placeholder.com/99x7/66ee55?text=%20" alt="99" />
|
||||
[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 Sun Apr 04 21:31:44 CEST 2021
|
||||
564
samples/NewConfig.yml
Normal file
564
samples/NewConfig.yml
Normal file
@ -0,0 +1,564 @@
|
||||
# =======================================================================================================
|
||||
# _____ __ .__ _____ __________ .__ .___ .___
|
||||
# / _ \ __ ___/ |_| |__ / \ ____\______ \ ____ | | _________ __| _/____ __| _/
|
||||
# / /_\ \| | \ __| | \ / \ / \_/ __ \| __/ __ \| | / _ \__ \ / __ _/ __ \ / __ |
|
||||
# / | | | /| | | Y / Y \ ___/| | \ ___/| |_( <_> / __ \/ /_/ \ ___// /_/ |
|
||||
# \____|__ |____/ |__| |___| \____|__ /\___ |____|_ /\___ |____/\____(____ \____ |\___ \____ |
|
||||
# \/ \/ \/ \/ \/ \/ \/ \/ \/ \/
|
||||
#
|
||||
# =======================================================================================================
|
||||
#
|
||||
# Authme Main Configuration File.
|
||||
#
|
||||
# =======================================================================================================
|
||||
|
||||
# Plugin infos (overwritten on start, just a simple way to find out your plugin version).
|
||||
authors: ${pluginAuthors}
|
||||
version: ${project.version}
|
||||
buildNumber: ${buildNumber}
|
||||
|
||||
# Set this setting to true when you have configured the plugin,
|
||||
# when false the server will be stopped with a warning message.
|
||||
enabled: false
|
||||
|
||||
# Database settings.
|
||||
data_source:
|
||||
# ===========================
|
||||
# Database general settings.
|
||||
# ===========================
|
||||
|
||||
# Database backend (sqlite, mysql).
|
||||
backend: sqlite
|
||||
# Enable database queries caching, should improve performance.
|
||||
caching: true
|
||||
|
||||
# ===========================
|
||||
# SqLite db parameters.
|
||||
# ===========================
|
||||
|
||||
sqlite:
|
||||
# The name of the database storage file.
|
||||
filename: 'authme.db'
|
||||
|
||||
# ===========================
|
||||
# MySql db parameters.
|
||||
# ===========================
|
||||
|
||||
mysql:
|
||||
# Connection parameters.
|
||||
host: '127.0.0.1'
|
||||
port: 3306
|
||||
username: 'change_me'
|
||||
password: 'change_me'
|
||||
database: 'my_minecraft_server'
|
||||
tablename: 'authme'
|
||||
|
||||
# Column names.
|
||||
column_names:
|
||||
id: id
|
||||
# Column for storing nicknames (ignore case nickname).
|
||||
name: username
|
||||
# Column for storing the realname (case sensitive nickname).
|
||||
real_name: realname
|
||||
# Column for storing passwords.
|
||||
password: password
|
||||
# Column for storing email addresses.
|
||||
email: email
|
||||
# Column for storing the authentication status (logged or not).
|
||||
login_status: isLogged
|
||||
# Column for storing player IPs.
|
||||
ip: ip
|
||||
# Column for storing lastlogins date and time.
|
||||
last_login_timestamp: lastlogin
|
||||
# Latest logout location of the players.
|
||||
last_location:
|
||||
world: world
|
||||
x: x
|
||||
y: y
|
||||
z: z
|
||||
# Enabled only if the bungeecord integration is activated.
|
||||
server: world
|
||||
|
||||
# Support for registrations via WebInterfaces/CSM.
|
||||
# Disable some backend caching parameters.
|
||||
disableAggressiveCaching: false
|
||||
|
||||
# Main settings
|
||||
settings:
|
||||
|
||||
# ===========================
|
||||
# Bungeecord integration
|
||||
# ===========================
|
||||
|
||||
bungeecord:
|
||||
# Enable bungeecord integration features
|
||||
enabled: true
|
||||
|
||||
# Server name (must be unique, please use the name in the bungeecord configuration).
|
||||
# Use 'auto' for auto configuration (requires the bungeecord module).
|
||||
serverName: LoginLobby1
|
||||
# Keep the auth status when the player moves between servers.
|
||||
# Required if you're using the bungeecord module.
|
||||
keepAuthBetweenServers: true
|
||||
|
||||
# Target server after login
|
||||
send_after_login:
|
||||
enabled: false
|
||||
message: ''
|
||||
delay: 5
|
||||
# Server name ("ServerName") or group ("G:GroupName")
|
||||
# Groups are avariable only when the bungeecord module is avariable.
|
||||
# If the server change fails the player will be kicked.
|
||||
target: Lobby1
|
||||
failKickMessage: 'Failed to connect to the lobby! Please try to join the server again!'
|
||||
# Target server after logout
|
||||
send_after_logout:
|
||||
enabled: false
|
||||
message: ''
|
||||
delay: 5
|
||||
# Server name ("ServerName") or group ("G:GroupName")
|
||||
# Groups are avariable only when the bungeecord module is avariable.
|
||||
# If the server change fails the player will be kicked.
|
||||
target: LoginLobby1
|
||||
failKickMessage: 'Failed to connect to the lobby! Please try to join the server again!'
|
||||
|
||||
# Variables:
|
||||
# %p playername
|
||||
bungee_commands:
|
||||
player_command_after_register:
|
||||
enabled: false
|
||||
cmd: ''
|
||||
console_command_after_register:
|
||||
enabled: false
|
||||
cmd: 'alert %p joined for the first time the network!'
|
||||
player_command_after_login:
|
||||
enabled: false
|
||||
cmd: 'glist'
|
||||
console_command_after_login:
|
||||
enabled: false
|
||||
cmd: 'alert %p logged in correctly!'
|
||||
player_command_after_join:
|
||||
enabled: false
|
||||
cmd: ''
|
||||
console_command_after_join:
|
||||
enabled: false
|
||||
cmd: 'alert %p joined the network!'
|
||||
player_command_first_join:
|
||||
enabled: false
|
||||
cmd: ''
|
||||
console_command_first_join:
|
||||
enabled: false
|
||||
cmd: 'alert %p joined for the first time the network!'
|
||||
|
||||
# ===========================
|
||||
# Sessions configuration.
|
||||
# ===========================
|
||||
|
||||
sessions:
|
||||
# Enable sessions.
|
||||
# When a player is authenticated, his IP and his nickname is saved.
|
||||
# The next time the player will join the server, if his IP is the same
|
||||
# of the last time, and the timeout time hasn't expired, he will be
|
||||
# automatically authenticated.
|
||||
enabled: false
|
||||
# Session timeout.
|
||||
# 0 for unlimited time (Very dangerous, use it at your own risk!)
|
||||
# Consider that if player's ip has changed but the timeout hasn't
|
||||
# expired, player will be kicked out of the sever!
|
||||
timeout: 10
|
||||
# When enabled a player's session will expire if someone tries to
|
||||
# login with a different IP Address.
|
||||
expire_on_ip_change: true
|
||||
|
||||
# ===========================
|
||||
# Registration settings.
|
||||
# ===========================
|
||||
|
||||
registration:
|
||||
# After how many time unregistered players should be kicked?
|
||||
# Set to 0 to disable. (default: 30)
|
||||
timeout: 30
|
||||
|
||||
nickname:
|
||||
min_length: 4
|
||||
max_lenght: 16
|
||||
# Regex syntax.
|
||||
allowed_characters: '[a-zA-Z0-9_]*'
|
||||
|
||||
password:
|
||||
# Enable double check of password on registration:
|
||||
# /register <password> <confirmPassword>
|
||||
double_check: true
|
||||
# Minimum password lenght.
|
||||
min_length: 5
|
||||
# Regex syntax.
|
||||
allowed_characters: '[\x21-\x7E]*'
|
||||
# Denied unsafe passwords.
|
||||
unsafePasswords:
|
||||
- '123456'
|
||||
- 'password'
|
||||
- 'qwerty'
|
||||
- '12345'
|
||||
- '54321'
|
||||
|
||||
# ===========================
|
||||
# Login settings.
|
||||
# ===========================
|
||||
|
||||
login:
|
||||
# After how many time unlogged players should be kicked?
|
||||
# Set to 0 to disable. (default: 30)
|
||||
timeout: 30
|
||||
|
||||
|
||||
|
||||
# ===========================
|
||||
# Encryption parameters.
|
||||
# ===========================
|
||||
|
||||
password_encryption:
|
||||
# The hashing algorithm.
|
||||
# Possible values: MD5, SHA1, SHA256, WHIRLPOOL, XAUTH, MD5VB, PHPBB, MYBB, IPB3,
|
||||
# PHPFUSION, SMF, XENFORO, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512, DOUBLEMD5,
|
||||
# PBKDF2, PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM (developpers only).
|
||||
encryption_algorithm: SHA256
|
||||
# The salt length for the SALTED2MD5 and MD5(MD5(password)+salt) algorithms.
|
||||
md5_salt_length: 8
|
||||
# If password check fails try all the other hash algorithm.
|
||||
# AuthMe will update the password to the new passwordHash.
|
||||
enable_convertion: false
|
||||
|
||||
# ===========================
|
||||
# Unlogged user restrictions.
|
||||
# ===========================
|
||||
|
||||
unlogged_restrictions:
|
||||
# Deny chat messages send for unlogged users.
|
||||
deny_chat: true
|
||||
# Hide chat to unlogged users.
|
||||
# Only player messages, plugins will be able to send messages to the player anyway.
|
||||
hide_chat: false
|
||||
|
||||
# Deny any command message not in the whitelist below.
|
||||
deny_commands: true
|
||||
command_whitelist:
|
||||
- /login
|
||||
- /register
|
||||
- /l
|
||||
- /reg
|
||||
- /email
|
||||
- /captcha
|
||||
|
||||
movements:
|
||||
# Restrict player movements.
|
||||
restrict: true
|
||||
# Allowed radius.
|
||||
allowed_radius: 0
|
||||
# Should unlogged players have speed = 0?
|
||||
# After the login the walking/flying speeed will be reset to the default value.
|
||||
removeSpeed: true
|
||||
|
||||
# End is there atm xD
|
||||
|
||||
# This option will save the quit location of the players.
|
||||
SaveQuitLocation: false
|
||||
# Should not logged in players be teleported to the spawn?
|
||||
# After the authentication, if SaveQuitLocation is enabled,
|
||||
# they will be teleported back to their normal position.
|
||||
teleportUnAuthedToSpawn: false
|
||||
|
||||
# If enabled, after the login, if the ForceSpawnOnTheseWorlds setting contains
|
||||
# the player's world, he will be teleported to the world spawnpoint.
|
||||
# The quit location of the player will be overwritten.
|
||||
# This is different from "teleportUnAuthedToSpawn" that teleports player
|
||||
# back to his quit location after the authentication.
|
||||
ForceSpawnLocOnJoinEnabled: false
|
||||
# WorldNames where we need to force the spawn location
|
||||
# Warning: This setting is Case Sensitive!
|
||||
ForceSpawnOnTheseWorlds:
|
||||
- world
|
||||
- world_nether
|
||||
- world_the_end
|
||||
|
||||
# this is very important options,
|
||||
# every time player join the server,
|
||||
# if they are registered, AuthMe will switch him
|
||||
# to unLoggedInGroup, this
|
||||
# should prevent all major exploit.
|
||||
# So you can set up on your Permission Plugin
|
||||
# this special group with 0 permissions, or permissions to chat,
|
||||
# or permission to
|
||||
# send private message or all other perms that you want,
|
||||
# the better way is to set up
|
||||
# this group with few permissions,
|
||||
# so if player try to exploit some account,
|
||||
# they can
|
||||
# do anything except what you set in perm Group.
|
||||
# After a correct logged-in player will be
|
||||
# moved to his correct permissions group!
|
||||
# Pay attention group name is case sensitive,
|
||||
# so Admin is different from admin,
|
||||
# otherwise your group will be wiped,
|
||||
# and player join in default group []!
|
||||
# Example unLoggedinGroup: NotLogged
|
||||
unLoggedinGroup: unLoggedinGroup
|
||||
|
||||
# ===========================
|
||||
# Address restrictions
|
||||
# ===========================
|
||||
|
||||
# Max number of registrations per IP (default: 1)
|
||||
maxRegPerIp: 1
|
||||
# Maximum allowed number of Logins per IP, 0 to disable (default: 0)
|
||||
maxLoginPerIp: 0
|
||||
# Maximum allowed number of Joins per IP, 0 to disable (default: 0)
|
||||
maxJoinPerIp: 0
|
||||
|
||||
# When this setting is enabled, online players can't be kicked out
|
||||
# due to "Logged in from another Location"
|
||||
# This setting will prevent potential security exploits.
|
||||
ForceSingleSession: true
|
||||
|
||||
# To activate the restricted user feature you need
|
||||
# to enable this option and configure the
|
||||
# AllowedRestrictedUser field.
|
||||
AllowRestrictedUser: false
|
||||
# The restricted user feature will kick players listed below
|
||||
# if they dont match of the defined ip address.
|
||||
# Example:
|
||||
# AllowedRestrictedUser:
|
||||
# - playername;127.0.0.1
|
||||
AllowedRestrictedUser:
|
||||
- playername;127.0.0.
|
||||
# Ban ip when the ip is not the ip registered in database
|
||||
banUnsafedIP: false
|
||||
|
||||
|
||||
|
||||
# ===============================
|
||||
# Other restrictions
|
||||
# ===============================
|
||||
|
||||
# Should we protect the player inventory before logging in?
|
||||
# Warning: Requires the latest version of ProtocolLib!
|
||||
ProtectInventoryBeforeLogIn: true
|
||||
|
||||
# Should unregistered players be kicked immediately?
|
||||
kickNonRegistered: false
|
||||
# Should players be kicked on wrong password?
|
||||
kickOnWrongPassword: false
|
||||
|
||||
# Should we display all other accounts of a player when he joins?
|
||||
# Required permission: authme.admin.accounts
|
||||
displayOtherAccounts: true
|
||||
|
||||
# ===============================
|
||||
# Restrictions compatibility
|
||||
# ===============================
|
||||
|
||||
# Spawn Priority. Avariable values : authme, essentials, multiverse, default
|
||||
spawnPriority: authme,essentials,multiverse,default
|
||||
# AuthMe will NEVER teleport players!
|
||||
noTeleport: false
|
||||
|
||||
GameMode:
|
||||
# Do you want to set player's gamemode to survival when he joins?
|
||||
# This enables also the settings below.
|
||||
ForceSurvivalMode: false
|
||||
# Do you want to reset player's inventory if player joins with creative mode?
|
||||
ResetInventoryIfCreative: false
|
||||
# Do you want to force the survival mode ONLY after the /login process?
|
||||
ForceOnlyAfterLogin: false
|
||||
|
||||
# sgdc3: Ok, our configuration is shit.... xD Today I will stop there
|
||||
|
||||
|
||||
registration:
|
||||
# enable registration on the server?
|
||||
enabled: true
|
||||
# Send every X seconds a message to a player to
|
||||
# remind him that he has to login/register
|
||||
messageInterval: 5
|
||||
# Only registered and logged in players can play.
|
||||
# See restrictions for exceptions
|
||||
force: true
|
||||
# Does we replace password registration by an Email registration method ?
|
||||
enableEmailRegistrationSystem: false
|
||||
# Enable double check of email when you register
|
||||
# when it's true, registration require that kind of command:
|
||||
# /register <email> <confirmEmail>
|
||||
doubleEmailCheck: false
|
||||
# Do we force kicking player after a successful registration ?
|
||||
# Do not use with login feature below
|
||||
forceKickAfterRegister: false
|
||||
# Does AuthMe need to enforce a /login after a successful registration ?
|
||||
forceLoginAfterRegister: false
|
||||
unrestrictions:
|
||||
# below you can list all your account name, that
|
||||
# AuthMe will ignore for registration or login, configure it
|
||||
# at your own risk!! Remember that if you are going to add
|
||||
# nickname with [], you have to delimit name with ' '.
|
||||
# this option add compatibility with BuildCraft and some
|
||||
# other mods.
|
||||
# It is CaseSensitive!
|
||||
UnrestrictedName: []
|
||||
# Message language, available : en, de, br, cz, pl, fr, ru, hu, sk, es, zhtw, fi, zhcn, lt, it, ko, pt
|
||||
messagesLanguage: en
|
||||
# Force these commands after /login, without any '/', use %p for replace with player name
|
||||
forceCommands: []
|
||||
# Force these commands after /login as a server console, without any '/', use %p for replace with player name
|
||||
forceCommandsAsConsole: []
|
||||
# Force these commands after /register, without any '/', use %p for replace with player name
|
||||
forceRegisterCommands: []
|
||||
# Force these commands after /register as a server console, without any '/', use %p for replace with player name
|
||||
forceRegisterCommandsAsConsole: []
|
||||
# Do we need to broadcast the welcome message to all server or only to the player? set true for server or false for player
|
||||
broadcastWelcomeMessage: false
|
||||
# Do we need to delay the join/leave message to be displayed only when the player is authenticated ?
|
||||
delayJoinMessage: false
|
||||
removeJoinMessage: false
|
||||
removeLeaveMessage: false
|
||||
# Do we need to add potion effect Blinding before login/register ?
|
||||
applyBlindEffect: false
|
||||
ExternalBoardOptions:
|
||||
# MySQL column for the salt , needed for some forum/cms support
|
||||
mySQLColumnSalt: ''
|
||||
# MySQL column for the group, needed for some forum/cms support
|
||||
mySQLColumnGroup: ''
|
||||
# -1 mean disabled. If u want that only
|
||||
# activated player can login in your server
|
||||
# u can put in this options the group number
|
||||
# of unactivated user, needed for some forum/cms support
|
||||
nonActivedUserGroup: -1
|
||||
# Other MySQL columns where we need to put the Username (case sensitive)
|
||||
mySQLOtherUsernameColumns: []
|
||||
# How much Log to Round needed in BCrypt(do not change it if you do not know what's your doing)
|
||||
bCryptLog2Round: 10
|
||||
# phpBB prefix defined during phpbb installation process
|
||||
phpbbTablePrefix: 'phpbb_'
|
||||
# phpBB activated group id , 2 is default registered group defined by phpbb
|
||||
phpbbActivatedGroupId: 2
|
||||
# WordPress prefix defined during WordPress installation process
|
||||
wordpressTablePrefix: 'wp_'
|
||||
permission:
|
||||
# Take care with this options, if you dont want
|
||||
# to use Vault and Group Switching of
|
||||
# AuthMe for unloggedIn players put true
|
||||
# below, default is false.
|
||||
EnablePermissionCheck: false
|
||||
BackupSystem:
|
||||
# Enable or Disable Automatic Backup
|
||||
ActivateBackup: false
|
||||
# set Backup at every start of Server
|
||||
OnServerStart: false
|
||||
# set Backup at every stop of Server
|
||||
OnServerStop: true
|
||||
# Windows only mysql installation Path
|
||||
MysqlWindowsPath: 'C:\\Program Files\\MySQL\\MySQL Server 5.1\\'
|
||||
Security:
|
||||
SQLProblem:
|
||||
# Stop the server if we can't contact the sql database
|
||||
# Take care with this, if you set that to false,
|
||||
# AuthMe automatically disable and the server is not protected!
|
||||
stopServer: true
|
||||
ReloadCommand:
|
||||
# /reload support
|
||||
useReloadCommandSupport: true
|
||||
console:
|
||||
# Remove spam console
|
||||
noConsoleSpam: false
|
||||
captcha:
|
||||
# Player need to put a captcha when he fails too lot the password
|
||||
useCaptcha: false
|
||||
# Max allowed tries before request a captcha
|
||||
maxLoginTry: 5
|
||||
# Captcha length
|
||||
captchaLength: 5
|
||||
Converter:
|
||||
Rakamak:
|
||||
# Rakamak file name
|
||||
fileName: users.rak
|
||||
# Rakamak use ip ?
|
||||
useIP: false
|
||||
# IP file name for rakamak
|
||||
ipFileName: UsersIp.rak
|
||||
CrazyLogin:
|
||||
# CrazyLogin database file
|
||||
fileName: accounts.db
|
||||
Email:
|
||||
# Email SMTP server host
|
||||
mailSMTP: smtp.gmail.com
|
||||
# Email SMTP server port
|
||||
mailPort: 465
|
||||
# Email account that send the mail
|
||||
mailAccount: ''
|
||||
# Email account password
|
||||
mailPassword: ''
|
||||
# Custom SenderName, that replace the mailAccount name in the email
|
||||
mailSenderName: ''
|
||||
# Random password length
|
||||
RecoveryPasswordLength: 8
|
||||
# Email subject of password get
|
||||
mailSubject: 'Your new AuthMe Password'
|
||||
# Email text here
|
||||
mailText: 'Dear <playername>, <br /><br /> This is your new AuthMe password for the server <br /><br /> <servername> : <br /><br /> <generatedpass><br /><br />Do not forget to change password after login! <br /> /changepassword <generatedpass> newPassword'
|
||||
# Like maxRegPerIp but with email
|
||||
maxRegPerEmail: 1
|
||||
# Recall players to add an email ?
|
||||
recallPlayers: false
|
||||
# Delay in minute for the recall scheduler
|
||||
delayRecall: 5
|
||||
# Blacklist these domains for emails
|
||||
emailBlacklisted:
|
||||
- 10minutemail.com
|
||||
# WhiteList only these domains for emails
|
||||
emailWhitelisted: []
|
||||
# Do we need to send new password draw in an image ?
|
||||
generateImage: false
|
||||
Hooks:
|
||||
# Do we need to hook with multiverse for spawn checking?
|
||||
multiverse: true
|
||||
# Do we need to hook with BungeeCord for get the real Player ip ?
|
||||
bungeecord: false
|
||||
# Do we need to disable Essentials SocialSpy on join ?
|
||||
disableSocialSpy: true
|
||||
# Do we need to force /motd Essentials command on join ?
|
||||
useEssentialsMotd: false
|
||||
# Do we need to cache custom Attributes ?
|
||||
customAttributes: false
|
||||
Purge:
|
||||
# On Enable , does AuthMe need to purge automatically old accounts unused ?
|
||||
useAutoPurge: false
|
||||
# Number of Days an account become Unused
|
||||
daysBeforeRemovePlayer: 60
|
||||
# Do we need to remove the player.dat file during purge process ?
|
||||
removePlayerDat: false
|
||||
# Do we need to remove the Essentials/users/player.yml file during purge process ?
|
||||
removeEssentialsFile: false
|
||||
# World where are players.dat stores
|
||||
defaultWorld: 'world'
|
||||
# Do we need to remove LimitedCreative/inventories/player.yml , player_creative.yml files during purge process ?
|
||||
removeLimitedCreativesInventories: false
|
||||
# Do we need to remove the AntiXRayData/PlayerData/player file during purge process ?
|
||||
removeAntiXRayFile: false
|
||||
# Do we need to remove permissions ?
|
||||
removePermissions: false
|
||||
Protection:
|
||||
# Enable some servers protection ( country based login, antibot )
|
||||
enableProtection: false
|
||||
# Countries allowed to join the server and register, see http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ for countries' codes
|
||||
countries:
|
||||
- US
|
||||
- GB
|
||||
# Countries blacklisted automatically ( without any needed to enable protection )
|
||||
countriesBlacklist:
|
||||
- A1
|
||||
# Do we need to enable automatic antibot system?
|
||||
enableAntiBot: false
|
||||
# Max number of player allowed to login in 5 secs before enable AntiBot system automatically
|
||||
antiBotSensibility: 5
|
||||
# Duration in minutes of the antibot automatic system
|
||||
antiBotDuration: 10
|
||||
VeryGames:
|
||||
# These features are only available on VeryGames Server Provider
|
||||
enableIpCheck: false
|
||||
199
samples/NewPlugin.yml
Normal file
199
samples/NewPlugin.yml
Normal file
@ -0,0 +1,199 @@
|
||||
name: ${pluginName}
|
||||
authors: [${pluginAuthors}]
|
||||
website: ${project.url}
|
||||
description: ${project.description}
|
||||
main: ${mainClass}
|
||||
version: ${project.version}-b${buildNumber}
|
||||
softdepend:
|
||||
- Vault
|
||||
- PermissionsBukkit
|
||||
- PermissionsEX
|
||||
- EssentialsGroupManager
|
||||
- Multiverse-Core
|
||||
- Essentials
|
||||
- EssentialsSpawn
|
||||
- ProtocolLib
|
||||
commands:
|
||||
authme:
|
||||
description: AuthMe admin commands
|
||||
usage: '/authme reload|register playername password|changepassword playername password|unregister playername|version|converter datatype'
|
||||
permission: authme.admin
|
||||
register:
|
||||
description: Register an account
|
||||
usage: /register password confirmpassword
|
||||
aliases: [reg]
|
||||
permission: authme.player.register
|
||||
login:
|
||||
description: Login into a account
|
||||
usage: /login password
|
||||
aliases: [l,log]
|
||||
permission: authme.player.login
|
||||
changepassword:
|
||||
description: Change password of a account
|
||||
usage: /changepassword oldPassword newPassword
|
||||
permission: authme.player.changepassword
|
||||
logout:
|
||||
description: Logout from the server
|
||||
usage: /logout
|
||||
permission: authme.player.logout
|
||||
unregister:
|
||||
description: unregister your account
|
||||
usage: /unregister password
|
||||
permission: authme.player.unregister
|
||||
email:
|
||||
description: Add Email or recover password
|
||||
usage: '/email add your@email.com your@email.com|change oldEmail newEmail|recovery your@email.com'
|
||||
permission: authme.player.email
|
||||
captcha:
|
||||
description: Captcha command
|
||||
usage: /captcha theCaptcha
|
||||
permission: authme.player.captcha
|
||||
permissions:
|
||||
authme.canbeforced:
|
||||
description: Allow the user to be forced-logged via API
|
||||
default: true
|
||||
authme.player:
|
||||
description: Gives access to all authme player commands
|
||||
default: true
|
||||
children:
|
||||
authme.player.login: true
|
||||
authme.player.logout: true
|
||||
authme.player.register: true
|
||||
authme.player.unregister: true
|
||||
authme.player.changepassword: true
|
||||
authme.player.captcha: true
|
||||
authme.player.email: true
|
||||
authme.player.register:
|
||||
description: Register your account
|
||||
default: false
|
||||
authme.player.unregister:
|
||||
description: Unregister your account
|
||||
default: false
|
||||
authme.player.login:
|
||||
description: Login into your account
|
||||
default: false
|
||||
authme.player.logout:
|
||||
description: Logout from your account
|
||||
default: false
|
||||
authme.player.changepassword:
|
||||
description: Change password of your account
|
||||
default: false
|
||||
authme.player.email:
|
||||
description: Gives access to player's email commands
|
||||
default: false
|
||||
children:
|
||||
authme.player.email.add: true
|
||||
authme.player.email.change: true
|
||||
authme.player.email.recover: true
|
||||
authme.player.email.add:
|
||||
description: Add an email to your account
|
||||
default: false
|
||||
authme.player.email.change:
|
||||
description: Change email of your account
|
||||
default: false
|
||||
authme.player.email.recover:
|
||||
description: Recover your account
|
||||
default: false
|
||||
authme.player.captcha:
|
||||
description: Captcha command
|
||||
default: false
|
||||
authme.admin:
|
||||
description: Gives access to all authme admin commands
|
||||
default: op
|
||||
children:
|
||||
authme.admin.forcelogin: true
|
||||
authme.admin.forcelogout: true
|
||||
authme.admin.register: true
|
||||
authme.admin.unregister: true
|
||||
authme.admin.changemail: true
|
||||
authme.admin.changepassword: true
|
||||
authme.admin.lastlogin: true
|
||||
authme.admin.accounts: true
|
||||
authme.admin.getemail: true
|
||||
authme.admin.getip: true
|
||||
authme.admin.setspawn: true
|
||||
authme.admin.spawn: true
|
||||
authme.admin.setfirstspawn: true
|
||||
authme.admin.firstspawn: true
|
||||
authme.admin.purge: true
|
||||
authme.admin.purgebannedplayers: true
|
||||
authme.admin.purgelastpos: true
|
||||
authme.admin.converter: true
|
||||
authme.admin.reload: true
|
||||
authme.admin.switchantibot: true
|
||||
authme.admin.seeotheraccounts: true
|
||||
authme.admin.register:
|
||||
description: Register an account
|
||||
default: false
|
||||
authme.admin.unregister:
|
||||
description: Unregister an account
|
||||
default: false
|
||||
authme.admin.forcelogin:
|
||||
description: Force login for that player
|
||||
default: false
|
||||
authme.admin.forcelogout:
|
||||
description: Force logout for that player
|
||||
default: false
|
||||
authme.admin.changepassword:
|
||||
description: Change the password of an account
|
||||
default: false
|
||||
authme.admin.getemail:
|
||||
description: Get last email about a player
|
||||
default: false
|
||||
authme.admin.changeemail:
|
||||
description: Change a player email
|
||||
default: false
|
||||
authme.admin.accounts:
|
||||
description: Display Players Accounts
|
||||
default: false
|
||||
authme.admin.seeotheraccounts:
|
||||
description: Display other accounts about a player when he logs in
|
||||
default: false
|
||||
authme.admin.lastlogin:
|
||||
description: Get last login date about a player
|
||||
default: false
|
||||
authme.admin.getip:
|
||||
description: Get IP from a player (fake and real)
|
||||
default: false
|
||||
authme.admin.setspawn:
|
||||
description: Set the AuthMe spawn point
|
||||
default: false
|
||||
authme.admin.spawn:
|
||||
description: Teleport to AuthMe spawn point
|
||||
default: false
|
||||
authme.admin.setfirstspawn:
|
||||
description: Set the AuthMe First Spawn Point
|
||||
default: false
|
||||
authme.admin.firstspawn:
|
||||
description: Teleport to AuthMe First Spawn Point
|
||||
default: false
|
||||
authme.admin.switchantibot:
|
||||
description: Switch AntiBot mode on/off
|
||||
default: false
|
||||
authme.admin.purge:
|
||||
description: Database purge command
|
||||
default: false
|
||||
authme.admin.purgebannedplayers:
|
||||
description: Purge banned players
|
||||
default: false
|
||||
authme.admin.purgelastpos:
|
||||
description: Purge last position of a player/players
|
||||
default: false
|
||||
authme.admin.converter:
|
||||
description: Allow the /authme converter command
|
||||
default: false
|
||||
authme.admin.reload:
|
||||
description: Reload the plugin
|
||||
default: false
|
||||
authme.vip:
|
||||
description: Allow vip slot when the server is full
|
||||
default: false
|
||||
authme.bypassantibot:
|
||||
description: Bypass the AntiBot check
|
||||
default: false
|
||||
authme.allowmultipleaccounts:
|
||||
description: Allow more accounts for same ip
|
||||
default: false
|
||||
authme.bypassforcesurvival:
|
||||
description: Bypass all ForceSurvival features
|
||||
default: false
|
||||
45798
samples/databases/xenforo.sql
Normal file
45798
samples/databases/xenforo.sql
Normal file
File diff suppressed because one or more lines are too long
148
samples/website_integration/AuthMeController.php
Normal file
148
samples/website_integration/AuthMeController.php
Normal file
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
/*****************************************************************************
|
||||
* AuthMe website integration logic *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* Allows interaction with the AuthMe database (registration, password *
|
||||
* verification). Don't forget to update the AUTHME_TABLE value and your *
|
||||
* database credentials in getAuthmeMySqli(). *
|
||||
* *
|
||||
* Source: https://github.com/AuthMe/AuthMeReloaded/ *
|
||||
*****************************************************************************/
|
||||
abstract class AuthMeController {
|
||||
|
||||
const AUTHME_TABLE = 'authme';
|
||||
|
||||
/**
|
||||
* Entry point function to check supplied credentials against the AuthMe database.
|
||||
*
|
||||
* @param string $username the username
|
||||
* @param string $password the password
|
||||
* @return bool true iff the data is correct, false otherwise
|
||||
*/
|
||||
function checkPassword($username, $password) {
|
||||
if (is_scalar($username) && is_scalar($password)) {
|
||||
$hash = $this->getHashFromDatabase($username);
|
||||
if ($hash) {
|
||||
return $this->isValidPassword($password, $hash);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the user exists in the database or not.
|
||||
*
|
||||
* @param string $username the username to check
|
||||
* @return bool true if the user exists; false otherwise
|
||||
*/
|
||||
function isUserRegistered($username) {
|
||||
$mysqli = $this->getAuthmeMySqli();
|
||||
if ($mysqli !== null) {
|
||||
$stmt = $mysqli->prepare('SELECT 1 FROM ' . self::AUTHME_TABLE . ' WHERE username = ?');
|
||||
$stmt->bind_param('s', $username);
|
||||
$stmt->execute();
|
||||
return $stmt->fetch();
|
||||
}
|
||||
|
||||
// Defensive default to true; we actually don't know
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a player with the given username.
|
||||
*
|
||||
* @param string $username the username to register
|
||||
* @param string $password the password to associate to the user
|
||||
* @param string $email the email (may be empty)
|
||||
* @return bool whether or not the registration was successful
|
||||
*/
|
||||
function register($username, $password, $email) {
|
||||
$email = $email ? $email : 'your@email.com';
|
||||
$mysqli = $this->getAuthmeMySqli();
|
||||
if ($mysqli !== null) {
|
||||
$hash = $this->hash($password);
|
||||
$stmt = $mysqli->prepare('INSERT INTO ' . self::AUTHME_TABLE . ' (username, realname, password, email, ip) '
|
||||
. 'VALUES (?, ?, ?, ?, ?)');
|
||||
$username_low = strtolower($username);
|
||||
$stmt->bind_param('sssss', $username_low, $username, $hash, $email, $_SERVER['REMOTE_ADDR']);
|
||||
return $stmt->execute();
|
||||
}
|
||||
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.
|
||||
*
|
||||
* @param $password string the clear-text password to hash
|
||||
* @return string the resulting hash
|
||||
*/
|
||||
protected abstract function hash($password);
|
||||
|
||||
/**
|
||||
* Checks whether the given password matches the hash.
|
||||
*
|
||||
* @param $password string the clear-text password
|
||||
* @param $hash string the password hash
|
||||
* @return boolean true if the password matches, false otherwise
|
||||
*/
|
||||
protected abstract function isValidPassword($password, $hash);
|
||||
|
||||
/**
|
||||
* Returns a connection to the database.
|
||||
*
|
||||
* @return mysqli|null the mysqli object or null upon error
|
||||
*/
|
||||
private function getAuthmeMySqli() {
|
||||
// CHANGE YOUR DATABASE DETAILS HERE BELOW: host, user, password, database name
|
||||
$mysqli = new mysqli('localhost', 'root', '', 'authme');
|
||||
if (mysqli_connect_error()) {
|
||||
printf('Could not connect to AuthMe database. Errno: %d, error: "%s"',
|
||||
mysqli_connect_errno(), mysqli_connect_error());
|
||||
return null;
|
||||
}
|
||||
return $mysqli;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the hash associated with the given user from the database.
|
||||
*
|
||||
* @param string $username the username whose hash should be retrieved
|
||||
* @return string|null the hash, or null if unavailable (e.g. username doesn't exist)
|
||||
*/
|
||||
private function getHashFromDatabase($username) {
|
||||
$mysqli = $this->getAuthmeMySqli();
|
||||
if ($mysqli !== null) {
|
||||
$stmt = $mysqli->prepare('SELECT password FROM ' . self::AUTHME_TABLE . ' WHERE username = ?');
|
||||
$stmt->bind_param('s', $username);
|
||||
$stmt->execute();
|
||||
$stmt->bind_result($password);
|
||||
if ($stmt->fetch()) {
|
||||
return $password;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
20
samples/website_integration/Bcrypt.php
Normal file
20
samples/website_integration/Bcrypt.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/***********************************************************
|
||||
* AuthMe website integration logic for BCrypt *
|
||||
* ------------------------------------------------------- *
|
||||
* See AuthMeController for details. *
|
||||
* *
|
||||
* Source: https://github.com/AuthMe/AuthMeReloaded/ *
|
||||
***********************************************************/
|
||||
class Bcrypt extends AuthMeController {
|
||||
|
||||
protected function hash($password) {
|
||||
return password_hash($password, PASSWORD_BCRYPT);
|
||||
}
|
||||
|
||||
protected function isValidPassword($password, $hash) {
|
||||
return password_verify($password, $hash);
|
||||
}
|
||||
|
||||
}
|
||||
53
samples/website_integration/Pbkdf2.php
Normal file
53
samples/website_integration/Pbkdf2.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/***********************************************************
|
||||
* AuthMe website integration logic for PBKDF2 *
|
||||
* ------------------------------------------------------- *
|
||||
* See AuthMeController for details. *
|
||||
* *
|
||||
* Source: https://github.com/AuthMe/AuthMeReloaded/ *
|
||||
***********************************************************/
|
||||
class Pbkdf2 extends AuthMeController {
|
||||
|
||||
/** @var string[] range of characters for salt generation */
|
||||
private $CHARS;
|
||||
|
||||
const SALT_LENGTH = 16;
|
||||
const NUMBER_OF_ITERATIONS = 10000;
|
||||
|
||||
public function __construct() {
|
||||
$this->CHARS = self::initCharRange();
|
||||
}
|
||||
|
||||
protected function isValidPassword($password, $hash) {
|
||||
// hash := pbkdf2_sha256$iterations$salt$hash
|
||||
$parts = explode('$', $hash);
|
||||
return count($parts) === 4 && $hash === $this->computeHash($parts[1], $parts[2], $password);
|
||||
}
|
||||
|
||||
protected function hash($password) {
|
||||
$salt = $this->generateSalt();
|
||||
return $this->computeHash(self::NUMBER_OF_ITERATIONS, $salt, $password);
|
||||
}
|
||||
|
||||
private function computeHash($iterations, $salt, $password) {
|
||||
return 'pbkdf2_sha256$' . self::NUMBER_OF_ITERATIONS . '$' . $salt
|
||||
. '$' . hash_pbkdf2('sha256', $password, $salt, self::NUMBER_OF_ITERATIONS, 64, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string randomly generated salt
|
||||
*/
|
||||
private function generateSalt() {
|
||||
$maxCharIndex = count($this->CHARS) - 1;
|
||||
$salt = '';
|
||||
for ($i = 0; $i < self::SALT_LENGTH; ++$i) {
|
||||
$salt .= $this->CHARS[mt_rand(0, $maxCharIndex)];
|
||||
}
|
||||
return $salt;
|
||||
}
|
||||
|
||||
private static function initCharRange() {
|
||||
return array_merge(range('0', '9'), range('a', 'f'));
|
||||
}
|
||||
}
|
||||
48
samples/website_integration/Sha256.php
Normal file
48
samples/website_integration/Sha256.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/***********************************************************
|
||||
* AuthMe website integration logic for SHA256 *
|
||||
* ------------------------------------------------------- *
|
||||
* See AuthMeController for details. *
|
||||
* *
|
||||
* Source: https://github.com/AuthMe/AuthMeReloaded/ *
|
||||
***********************************************************/
|
||||
class Sha256 extends AuthMeController {
|
||||
|
||||
/** @var string[] range of characters for salt generation */
|
||||
private $CHARS;
|
||||
|
||||
const SALT_LENGTH = 16;
|
||||
|
||||
public function __construct() {
|
||||
$this->CHARS = self::initCharRange();
|
||||
}
|
||||
|
||||
protected function isValidPassword($password, $hash) {
|
||||
// $SHA$salt$hash, where hash := sha256(sha256(password) . salt)
|
||||
$parts = explode('$', $hash);
|
||||
return count($parts) === 4 && $parts[3] === hash('sha256', hash('sha256', $password) . $parts[2]);
|
||||
}
|
||||
|
||||
protected function hash($password) {
|
||||
$salt = $this->generateSalt();
|
||||
return '$SHA$' . $salt . '$' . hash('sha256', hash('sha256', $password) . $salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string randomly generated salt
|
||||
*/
|
||||
private function generateSalt() {
|
||||
$maxCharIndex = count($this->CHARS) - 1;
|
||||
$salt = '';
|
||||
for ($i = 0; $i < self::SALT_LENGTH; ++$i) {
|
||||
$salt .= $this->CHARS[mt_rand(0, $maxCharIndex)];
|
||||
}
|
||||
return $salt;
|
||||
}
|
||||
|
||||
private static function initCharRange() {
|
||||
return array_merge(range('0', '9'), range('a', 'f'));
|
||||
}
|
||||
|
||||
}
|
||||
102
samples/website_integration/index.php
Normal file
102
samples/website_integration/index.php
Normal file
@ -0,0 +1,102 @@
|
||||
<!--
|
||||
This is a demo page for AuthMe website integration.
|
||||
See AuthMeController.php and the extending classes for the PHP code you need.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>AuthMe Integration Sample</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require 'AuthMeController.php';
|
||||
|
||||
// Change this to the file of the hash encryption you need, e.g. Bcrypt.php or Sha256.php
|
||||
require 'Sha256.php';
|
||||
// The class name must correspond to the file you have in require above! e.g. require 'Sha256.php'; and new Sha256();
|
||||
$authme_controller = new Sha256();
|
||||
|
||||
$action = get_from_post_or_empty('action');
|
||||
$user = get_from_post_or_empty('username');
|
||||
$pass = get_from_post_or_empty('password');
|
||||
$email = get_from_post_or_empty('email');
|
||||
|
||||
$was_successful = false;
|
||||
if ($action && $user && $pass) {
|
||||
if ($action === 'Log in') {
|
||||
$was_successful = process_login($user, $pass, $authme_controller);
|
||||
} else if ($action === 'Register') {
|
||||
$was_successful = process_register($user, $pass, $email, $authme_controller);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$was_successful) {
|
||||
echo '<h1>Login sample</h1>
|
||||
This is a demo form for AuthMe website integration. Enter your AuthMe login details
|
||||
into the following form to test it.
|
||||
<form method="post">
|
||||
<table>
|
||||
<tr><td>Name</td><td><input type="text" value="' . htmlspecialchars($user) . '" name="username" /></td></tr>
|
||||
<tr><td>Email</td><td><input type="text" value="' . htmlspecialchars($email) . '" name="email" /></td></tr>
|
||||
<tr><td>Pass</td><td><input type="password" value="' . htmlspecialchars($pass) . '" name="password" /></td></tr>
|
||||
<tr>
|
||||
<td><input type="submit" name="action" value="Log in" /></td>
|
||||
<td><input type="submit" name="action" value="Register" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>';
|
||||
}
|
||||
|
||||
function get_from_post_or_empty($index_name) {
|
||||
return trim(
|
||||
filter_input(INPUT_POST, $index_name, FILTER_UNSAFE_RAW, FILTER_REQUIRE_SCALAR | FILTER_FLAG_STRIP_LOW)
|
||||
?: '');
|
||||
}
|
||||
|
||||
|
||||
// Login logic
|
||||
function process_login($user, $pass, AuthMeController $controller) {
|
||||
if ($controller->checkPassword($user, $pass)) {
|
||||
printf('<h1>Hello, %s!</h1>', htmlspecialchars($user));
|
||||
echo 'Successful login. Nice to have you back!'
|
||||
. '<br /><a href="index.php">Back to form</a>';
|
||||
return true;
|
||||
} else {
|
||||
echo '<h1>Error</h1> Invalid username or password.';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Register logic
|
||||
function process_register($user, $pass, $email, AuthMeController $controller) {
|
||||
if ($controller->isUserRegistered($user)) {
|
||||
echo '<h1>Error</h1> This user already exists.';
|
||||
} else if (!is_email_valid($email)) {
|
||||
echo '<h1>Error</h1> The supplied email is invalid.';
|
||||
} else {
|
||||
// Note that we don't validate the password or username at all in this demo...
|
||||
$register_success = $controller->register($user, $pass, $email);
|
||||
if ($register_success) {
|
||||
printf('<h1>Welcome, %s!</h1>Thanks for registering', htmlspecialchars($user));
|
||||
echo '<br /><a href="index.php">Back to form</a>';
|
||||
return true;
|
||||
} else {
|
||||
echo '<h1>Error</h1>Unfortunately, there was an error during the registration.';
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function is_email_valid($email) {
|
||||
return trim($email) === ''
|
||||
? true // accept no email
|
||||
: filter_var($email, FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
510
src/main/java/fr/xephi/authme/AuthMe.java
Normal file
510
src/main/java/fr/xephi/authme/AuthMe.java
Normal file
@ -0,0 +1,510 @@
|
||||
package fr.xephi.authme;
|
||||
|
||||
import ch.jalu.injector.Injector;
|
||||
import ch.jalu.injector.InjectorBuilder;
|
||||
import fr.xephi.authme.api.v3.AuthMeApi;
|
||||
import fr.xephi.authme.command.CommandHandler;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.initialization.DataFolder;
|
||||
import fr.xephi.authme.initialization.DataSourceProvider;
|
||||
import fr.xephi.authme.initialization.OnShutdownPlayerSaver;
|
||||
import fr.xephi.authme.initialization.OnStartupTasks;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
|
||||
import fr.xephi.authme.initialization.SettingsProvider;
|
||||
import fr.xephi.authme.initialization.TaskCloser;
|
||||
import fr.xephi.authme.listener.BlockListener;
|
||||
import fr.xephi.authme.listener.EntityListener;
|
||||
import fr.xephi.authme.listener.PlayerListener;
|
||||
import fr.xephi.authme.listener.PlayerListener111;
|
||||
import fr.xephi.authme.listener.PlayerListener19;
|
||||
import fr.xephi.authme.listener.PlayerListener19Spigot;
|
||||
import fr.xephi.authme.listener.PlayerQuitListener;
|
||||
import fr.xephi.authme.listener.GuiCaptchaHandler;
|
||||
import fr.xephi.authme.listener.ServerListener;
|
||||
import fr.xephi.authme.mail.EmailService;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.security.crypts.Sha256;
|
||||
import fr.xephi.authme.service.BackupService;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.MigrationService;
|
||||
import fr.xephi.authme.service.bungeecord.BungeeReceiver;
|
||||
import fr.xephi.authme.service.yaml.YamlParseException;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.SettingsWarner;
|
||||
import fr.xephi.authme.settings.properties.EmailSettings;
|
||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||
import fr.xephi.authme.task.CleanupTask;
|
||||
import fr.xephi.authme.task.purge.PurgeService;
|
||||
import fr.xephi.authme.util.ExceptionUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.net.URL;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Scanner;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE;
|
||||
import static fr.xephi.authme.util.Utils.isClassLoaded;
|
||||
|
||||
/**
|
||||
* The AuthMe main class.
|
||||
*/
|
||||
public class AuthMe extends JavaPlugin {
|
||||
|
||||
// Constants
|
||||
private static final String PLUGIN_NAME = "AuthMeReloaded";
|
||||
private static final String LOG_FILENAME = "authme.log";
|
||||
private static final int CLEANUP_INTERVAL = 5 * TICKS_PER_MINUTE;
|
||||
|
||||
// Version and build number values
|
||||
private static String pluginVersion = "5.6.0-Fork";
|
||||
private static final String pluginBuild = "b";
|
||||
private static String pluginBuildNumber = "22";
|
||||
protected final Boolean SHAEnabled = false;
|
||||
// Private instances
|
||||
private EmailService emailService;
|
||||
private CommandHandler commandHandler;
|
||||
@Inject
|
||||
public static Settings settings;
|
||||
private DataSource database;
|
||||
private BukkitService bukkitService;
|
||||
private Injector injector;
|
||||
private BackupService backupService;
|
||||
private ConsoleLogger logger;
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public AuthMe() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the plugin's name.
|
||||
*
|
||||
* @return The plugin's name.
|
||||
*/
|
||||
public static String getPluginName() {
|
||||
return PLUGIN_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin's version.
|
||||
*
|
||||
* @return The plugin's version.
|
||||
*/
|
||||
public static String getPluginVersion() {
|
||||
return pluginVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin's build number.
|
||||
*
|
||||
* @return The plugin's build number.
|
||||
*/
|
||||
public static String getPluginBuildNumber() {
|
||||
return pluginBuildNumber;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method called when the server enables the plugin.
|
||||
*/
|
||||
@Override
|
||||
public void onEnable() {
|
||||
|
||||
// Load the plugin version data from the plugin description file
|
||||
loadPluginInfo(getDescription().getVersion());
|
||||
|
||||
// Set the Logger instance and log file path
|
||||
ConsoleLogger.initialize(getLogger(), new File(getDataFolder(), LOG_FILENAME));
|
||||
logger = ConsoleLoggerFactory.get(AuthMe.class);
|
||||
logger.info("You are running an unofficial fork version of AuthMe!");
|
||||
|
||||
// Check server version
|
||||
if (!isClassLoaded("org.spigotmc.event.player.PlayerSpawnLocationEvent")
|
||||
|| !isClassLoaded("org.bukkit.event.player.PlayerInteractAtEntityEvent")) {
|
||||
logger.warning("你正在运行不受支持的服务器版本 (" + getServerNameVersionSafe() + "). "
|
||||
+ "AuthMe 仅支持Spigot 1.9及之后的版本!");
|
||||
stopOrUnload();
|
||||
return;
|
||||
}
|
||||
// Prevent running AuthMeBridge due to major exploit issues
|
||||
if (getServer().getPluginManager().isPluginEnabled("AuthMeBridge")) {
|
||||
logger.warning("检测到 AuthMeBridge被加载, 对AuthMeBridge的支持已经停止 "
|
||||
+ "且可能会造成严重漏洞! 已中止加载!");
|
||||
stopOrUnload();
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the plugin
|
||||
try {
|
||||
initialize();
|
||||
} catch (Throwable th) {
|
||||
YamlParseException yamlParseException = ExceptionUtils.findThrowableInCause(YamlParseException.class, th);
|
||||
if (yamlParseException == null) {
|
||||
logger.logException("已中止AuthMeReReloaded的初始化,原因:", th);
|
||||
th.printStackTrace();
|
||||
} else {
|
||||
logger.logException("文件 '" + yamlParseException.getFile() + "' 包含YAML语法错误. "
|
||||
+ "请尝试在 https://yamllint.com 中检查文件内容", yamlParseException);
|
||||
}
|
||||
stopOrUnload();
|
||||
return;
|
||||
}
|
||||
|
||||
// Show settings warnings
|
||||
injector.getSingleton(SettingsWarner.class).logWarningsForMisconfigurations();
|
||||
|
||||
// Schedule clean up task
|
||||
CleanupTask cleanupTask = injector.getSingleton(CleanupTask.class);
|
||||
cleanupTask.runTaskTimerAsynchronously(this, CLEANUP_INTERVAL, CLEANUP_INTERVAL);
|
||||
// Do a backup on start
|
||||
backupService.doBackup(BackupService.BackupCause.START);
|
||||
|
||||
// Set up Metrics
|
||||
OnStartupTasks.sendMetrics(this, settings);
|
||||
// Successful message
|
||||
//detect server brand with classloader
|
||||
checkServerType();
|
||||
logger.info("AuthMeReReloaded is enabled successfully!");
|
||||
// Purge on start if enabled
|
||||
PurgeService purgeService = injector.getSingleton(PurgeService.class);
|
||||
purgeService.runAutoPurge();
|
||||
// 注册玩家退出事件监听
|
||||
if (settings.getProperty(SecuritySettings.ANTI_GHOST_PLAYERS) || settings.getProperty(SecuritySettings.SMART_ASYNC_TELEPORT)/* || settings.getProperty(SecuritySettings.GUI_CAPTCHA)*/) {
|
||||
if (settings.getProperty(SecuritySettings.ANTI_GHOST_PLAYERS)) {
|
||||
getServer().getPluginManager().registerEvents(new PlayerQuitListener((Plugin) this), this);
|
||||
}
|
||||
if (settings.getProperty(SecuritySettings.GUI_CAPTCHA) && getServer().getPluginManager().isPluginEnabled("ProtocolLib")) {
|
||||
getServer().getPluginManager().registerEvents(new GuiCaptchaHandler((Plugin) this), this);
|
||||
logger.info("(Alpha3)GUICaptcha Feature is enabled successfully!");
|
||||
logger.info("These features are still in development, if you encountered any problem, please report.");
|
||||
|
||||
} else if (settings.getProperty(SecuritySettings.GUI_CAPTCHA) && !getServer().getPluginManager().isPluginEnabled("ProtocolLib")) {
|
||||
logger.warning("ProtocolLib is not loaded, we can't enable GUI Captcha.");
|
||||
}
|
||||
//logger.info("以上功能尚在测试中,如有问题请反馈,如需关闭请前往config.yml修改");
|
||||
logger.info("GitHub Issue: github.com/HaHaWTH/AuthMeReReloaded/issues");
|
||||
}
|
||||
if (settings.getProperty(SecuritySettings.CHECK_FOR_UPDATES)) {
|
||||
checkForUpdates();
|
||||
}
|
||||
|
||||
if (SHAEnabled){
|
||||
//shaChecker();
|
||||
}
|
||||
}
|
||||
public File pluginfile = getFile();
|
||||
/**
|
||||
* Load the version and build number of the plugin from the description file.
|
||||
*
|
||||
* @param versionRaw the version as given by the plugin description file
|
||||
*/
|
||||
|
||||
private static void loadPluginInfo(String versionRaw) {
|
||||
int index = versionRaw.lastIndexOf("-");
|
||||
if (index != -1) {
|
||||
pluginVersion = versionRaw.substring(0, index);
|
||||
pluginBuildNumber = versionRaw.substring(index + 1);
|
||||
if (pluginBuildNumber.startsWith("b")) {
|
||||
pluginBuildNumber = pluginBuildNumber.substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the plugin and all the services.
|
||||
*/
|
||||
private void initialize() {
|
||||
// Create plugin folder
|
||||
getDataFolder().mkdir();
|
||||
|
||||
// Create injector, provide elements from the Bukkit environment and register providers
|
||||
injector = new InjectorBuilder()
|
||||
.addDefaultHandlers("fr.xephi.authme")
|
||||
.create();
|
||||
injector.register(AuthMe.class, this);
|
||||
injector.register(Server.class, getServer());
|
||||
injector.register(PluginManager.class, getServer().getPluginManager());
|
||||
injector.register(BukkitScheduler.class, getServer().getScheduler());
|
||||
injector.provide(DataFolder.class, getDataFolder());
|
||||
injector.registerProvider(Settings.class, SettingsProvider.class);
|
||||
injector.registerProvider(DataSource.class, DataSourceProvider.class);
|
||||
|
||||
// Get settings and set up logger
|
||||
settings = injector.getSingleton(Settings.class);
|
||||
ConsoleLoggerFactory.reloadSettings(settings);
|
||||
OnStartupTasks.setupConsoleFilter(getLogger());
|
||||
|
||||
// Set all service fields on the AuthMe class
|
||||
instantiateServices(injector);
|
||||
|
||||
// Convert deprecated PLAINTEXT hash entries
|
||||
MigrationService.changePlainTextToSha256(settings, database, new Sha256());
|
||||
|
||||
// If the server is empty (fresh start) just set all the players as unlogged
|
||||
if (bukkitService.getOnlinePlayers().isEmpty()) {
|
||||
database.purgeLogged();
|
||||
}
|
||||
|
||||
// Register event listeners
|
||||
registerEventListeners(injector);
|
||||
|
||||
// Start Email recall task if needed
|
||||
OnStartupTasks onStartupTasks = injector.newInstance(OnStartupTasks.class);
|
||||
onStartupTasks.scheduleRecallEmailTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates all services.
|
||||
*
|
||||
* @param injector the injector
|
||||
*/
|
||||
void instantiateServices(Injector injector) {
|
||||
database = injector.getSingleton(DataSource.class);
|
||||
bukkitService = injector.getSingleton(BukkitService.class);
|
||||
commandHandler = injector.getSingleton(CommandHandler.class);
|
||||
emailService = injector.getSingleton(EmailService.class);
|
||||
backupService = injector.getSingleton(BackupService.class);
|
||||
|
||||
// Trigger instantiation (class not used elsewhere)
|
||||
injector.getSingleton(BungeeReceiver.class);
|
||||
|
||||
// Trigger construction of API classes; they will keep track of the singleton
|
||||
injector.getSingleton(AuthMeApi.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all event listeners.
|
||||
*
|
||||
* @param injector the injector
|
||||
*/
|
||||
void registerEventListeners(Injector injector) {
|
||||
// Get the plugin manager instance
|
||||
PluginManager pluginManager = getServer().getPluginManager();
|
||||
|
||||
// Register event listeners
|
||||
pluginManager.registerEvents(injector.getSingleton(PlayerListener.class), this);
|
||||
pluginManager.registerEvents(injector.getSingleton(BlockListener.class), this);
|
||||
pluginManager.registerEvents(injector.getSingleton(EntityListener.class), this);
|
||||
pluginManager.registerEvents(injector.getSingleton(ServerListener.class), this);
|
||||
|
||||
// Try to register 1.9 player listeners
|
||||
if (isClassLoaded("org.bukkit.event.player.PlayerSwapHandItemsEvent")) {
|
||||
pluginManager.registerEvents(injector.getSingleton(PlayerListener19.class), this);
|
||||
}
|
||||
|
||||
// Try to register 1.9 spigot player listeners
|
||||
if (isClassLoaded("org.spigotmc.event.player.PlayerSpawnLocationEvent")) {
|
||||
pluginManager.registerEvents(injector.getSingleton(PlayerListener19Spigot.class), this);
|
||||
}
|
||||
|
||||
// Register listener for 1.11 events if available
|
||||
if (isClassLoaded("org.bukkit.event.entity.EntityAirChangeEvent")) {
|
||||
pluginManager.registerEvents(injector.getSingleton(PlayerListener111.class), this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the server or disables the plugin, as defined in the configuration.
|
||||
*/
|
||||
public void stopOrUnload() {
|
||||
if (settings == null || settings.getProperty(SecuritySettings.STOP_SERVER_ON_PROBLEM)) {
|
||||
getLogger().warning("THE SERVER IS GOING TO SHUT DOWN AS DEFINED IN THE CONFIGURATION!");
|
||||
setEnabled(false);
|
||||
getServer().shutdown();
|
||||
} else {
|
||||
setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
// onDisable is also called when we prematurely abort, so any field may be null
|
||||
OnShutdownPlayerSaver onShutdownPlayerSaver = injector == null
|
||||
? null
|
||||
: injector.createIfHasDependencies(OnShutdownPlayerSaver.class);
|
||||
if (onShutdownPlayerSaver != null) {
|
||||
onShutdownPlayerSaver.saveAllPlayers();
|
||||
}
|
||||
if (settings.getProperty(EmailSettings.SHUTDOWN_MAIL)){
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy'.'MM'.'dd'.' HH:mm:ss");
|
||||
Date date = new Date(System.currentTimeMillis());
|
||||
emailService.sendShutDown(settings.getProperty(EmailSettings.SHUTDOWN_MAIL_ADDRESS),dateFormat.format(date));
|
||||
}
|
||||
|
||||
// Do backup on stop if enabled
|
||||
if (backupService != null) {
|
||||
backupService.doBackup(BackupService.BackupCause.STOP);
|
||||
}
|
||||
|
||||
// Wait for tasks and close data source
|
||||
new TaskCloser(this, database).run();
|
||||
|
||||
// Disabled correctly
|
||||
Consumer<String> infoLogMethod = logger == null ? getLogger()::info : logger::info;
|
||||
infoLogMethod.accept("AuthMe " + this.getDescription().getVersion() + " is unloaded successfully!");
|
||||
ConsoleLogger.closeFileWriter();
|
||||
}
|
||||
private static final String owner = "HaHaWTH";
|
||||
private static final String owner_gitee = "Shixuehan114514";
|
||||
private static final String repo = "AuthMeReReloaded";
|
||||
private void checkForUpdates() {
|
||||
logger.info("Checking for updates...");
|
||||
Bukkit.getScheduler().runTaskAsynchronously(this, () -> {
|
||||
try {
|
||||
// 从南通集线器获取最新版本号
|
||||
|
||||
URL url = new URL("https://api.github.com/repos/" + owner + "/" + repo + "/releases/latest");
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setConnectTimeout(10000); // 设置连接超时为10秒
|
||||
conn.setReadTimeout(10000); // 设置读取超时为10秒
|
||||
Scanner scanner = new Scanner(conn.getInputStream());
|
||||
String response = scanner.useDelimiter("\\Z").next();
|
||||
scanner.close();
|
||||
|
||||
// 处理JSON响应
|
||||
String latestVersion = response.substring(response.indexOf("tag_name") + 11);
|
||||
latestVersion = latestVersion.substring(0, latestVersion.indexOf("\""));
|
||||
if ((pluginBuild + pluginBuildNumber).equals(latestVersion)) {
|
||||
getLogger().log(Level.INFO,"You are running the latest version.");
|
||||
}
|
||||
if (!(pluginBuild + pluginBuildNumber).equals(latestVersion)) {
|
||||
// Display update message
|
||||
String message = "New version available! Latest:" + latestVersion + " Current:" + pluginBuild + pluginBuildNumber;
|
||||
getLogger().log(Level.INFO, message);
|
||||
getLogger().log(Level.INFO,"Download from here:github.com/HaHaWTH/AuthMeReReloaded/releases/latest");
|
||||
}
|
||||
}catch (IOException e) {
|
||||
getLogger().log(Level.WARNING,"Error occurred while checking updates from GitHub. Reason: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void checkServerType() {
|
||||
if (isClassLoaded("com.destroystokyo.paper.PaperConfig")) {
|
||||
logger.info("AuthMeReReloaded is running on Paper");
|
||||
}else if (isClassLoaded("catserver.server.CatServerConfig")) {
|
||||
logger.info("AuthMeReReloaded is running on CatServer");
|
||||
} else if (isClassLoaded("org.spigotmc.SpigotConfig")) {
|
||||
logger.info("AuthMeReReloaded is running on Spigot");
|
||||
} else if (isClassLoaded("org.bukkit.craftbukkit.CraftServer")) {
|
||||
logger.info("AuthMeReReloaded is running on Bukkit");
|
||||
} else {
|
||||
logger.info("AuthMeReReloaded is running on Unknown*");
|
||||
}
|
||||
}
|
||||
// 其他方法和事件处理
|
||||
|
||||
|
||||
// 其他方法和事件处理
|
||||
private static final String SHA_URL = "https://raw.githubusercontent.com/"+ owner +"/"+ repo + "/master/"+pluginBuild +pluginBuildNumber+ ".sha";
|
||||
private static final String ALGORITHM = "SHA-256";
|
||||
private static final String PROXY_URL = "https://ghproxy.com/";
|
||||
private static final String SHA_URL_GITEE = "https://gitee.com/"+ owner_gitee +"/"+ repo + "/raw/master/"+pluginBuild+pluginBuildNumber+ ".sha";
|
||||
|
||||
// public void shaChecker() {
|
||||
// // 请求SHA文件
|
||||
//
|
||||
// String actualSha;
|
||||
// try {
|
||||
// URL url;
|
||||
// if(settings.getProperty(SecuritySettings.SHA_CHECK_METHOD).equals("github")) {
|
||||
// url = new URL(SHA_URL);
|
||||
// logger.info("正在检查文件完整性...(GitHub)");
|
||||
// } else if(settings.getProperty(SecuritySettings.SHA_CHECK_METHOD).equals("ghproxy")) {
|
||||
// url = new URL(PROXY_URL + SHA_URL);
|
||||
// logger.info("正在检查文件完整性...(GhProxy)");
|
||||
// } else if (settings.getProperty(SecuritySettings.SHA_CHECK_METHOD).equals("gitee")) {
|
||||
// url = new URL(SHA_URL_GITEE);
|
||||
// logger.info("正在检查文件完整性...(Gitee)");
|
||||
// }else {
|
||||
// logger.warning("未知的SHA检查方法,将从GitHub获取SHA文件");
|
||||
// url = new URL(SHA_URL);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
// conn.setConnectTimeout(10000);
|
||||
// conn.setReadTimeout(9000);
|
||||
// conn.setRequestMethod("GET");
|
||||
// InputStream stream = conn.getInputStream();
|
||||
// ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
// byte[] buffer = new byte[1024];
|
||||
// int length;
|
||||
// while ((length = stream.read(buffer)) != -1) {
|
||||
// result.write(buffer, 0, length);
|
||||
// }
|
||||
// String expectedSha = result.toString().trim();
|
||||
// // 计算插件文件的SHA值
|
||||
// MessageDigest md = MessageDigest.getInstance(ALGORITHM);
|
||||
// byte[] fileBytes = Files.readAllBytes(pluginfile.toPath());
|
||||
// byte[] hashBytes = md.digest(fileBytes);
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
// for (byte b : hashBytes) {
|
||||
// sb.append(String.format("%02x", b));
|
||||
// }
|
||||
// actualSha = sb.toString();
|
||||
//
|
||||
// // 比较SHA值并加载插件
|
||||
// if (expectedSha.equals(actualSha)) {
|
||||
// logger.info("SHA联网安全校验完毕");
|
||||
// } else {
|
||||
// // SHA值不匹配,插件可能被篡改
|
||||
// logger.warning("SHA值不匹配,插件被篡改");
|
||||
// stopOrUnload();
|
||||
// }
|
||||
// }catch (NoSuchAlgorithmException | IOException e){
|
||||
// logger.warning("SHA校验失败,请尝试切换校验API");
|
||||
// logger.warning("您当前请求的API为:" + settings.getProperty(SecuritySettings.SHA_CHECK_METHOD));
|
||||
// stopOrUnload();
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* Handle Bukkit commands.
|
||||
*
|
||||
* @param sender The command sender (Bukkit).
|
||||
* @param cmd The command (Bukkit).
|
||||
* @param commandLabel The command label (Bukkit).
|
||||
* @param args The command arguments (Bukkit).
|
||||
* @return True if the command was executed, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd,
|
||||
@NotNull String commandLabel, String[] args) {
|
||||
// Make sure the command handler has been initialized
|
||||
if (commandHandler == null) {
|
||||
getLogger().severe("AuthMe command handler is not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle the command
|
||||
return commandHandler.processCommand(sender, commandLabel, args);
|
||||
}
|
||||
|
||||
private String getServerNameVersionSafe() {
|
||||
try {
|
||||
Server server = getServer();
|
||||
return server.getName() + " v. " + server.getVersion();
|
||||
} catch (Throwable ignore) {
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
}
|
||||
287
src/main/java/fr/xephi/authme/ConsoleLogger.java
Normal file
287
src/main/java/fr/xephi/authme/ConsoleLogger.java
Normal file
@ -0,0 +1,287 @@
|
||||
package fr.xephi.authme;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import fr.xephi.authme.output.LogLevel;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.PluginSettings;
|
||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||
import fr.xephi.authme.util.ExceptionUtils;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* AuthMe logger.
|
||||
*/
|
||||
public final class ConsoleLogger {
|
||||
|
||||
private static final String NEW_LINE = System.getProperty("line.separator");
|
||||
/** Formatter which formats dates to something like "[08-16 21:18:46]" for any given LocalDateTime. */
|
||||
private static final DateTimeFormatter DATE_FORMAT = new DateTimeFormatterBuilder()
|
||||
.appendLiteral('[')
|
||||
.appendPattern("MM-dd HH:mm:ss")
|
||||
.appendLiteral(']')
|
||||
.toFormatter();
|
||||
|
||||
// Outside references
|
||||
private static File logFile;
|
||||
private static Logger logger;
|
||||
|
||||
// Shared state
|
||||
private static OutputStreamWriter fileWriter;
|
||||
|
||||
// Individual state
|
||||
private final String name;
|
||||
private LogLevel logLevel = LogLevel.INFO;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param name the name of this logger (the fully qualified class name using it)
|
||||
*/
|
||||
public ConsoleLogger(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// --------
|
||||
// Configurations
|
||||
// --------
|
||||
|
||||
public static void initialize(Logger logger, File logFile) {
|
||||
ConsoleLogger.logger = logger;
|
||||
ConsoleLogger.logFile = logFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets logging settings which are shared by all logger instances.
|
||||
*
|
||||
* @param settings the settings to read from
|
||||
*/
|
||||
public static void initializeSharedSettings(Settings settings) {
|
||||
boolean useLogging = settings.getProperty(SecuritySettings.USE_LOGGING);
|
||||
if (useLogging) {
|
||||
initializeFileWriter();
|
||||
} else {
|
||||
closeFileWriter();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets logging settings which are individual to all loggers.
|
||||
*
|
||||
* @param settings the settings to read from
|
||||
*/
|
||||
public void initializeSettings(Settings settings) {
|
||||
this.logLevel = settings.getProperty(PluginSettings.LOG_LEVEL);
|
||||
}
|
||||
|
||||
public LogLevel getLogLevel() {
|
||||
return logLevel;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
// --------
|
||||
// Logging methods
|
||||
// --------
|
||||
|
||||
/**
|
||||
* Log a WARN message.
|
||||
*
|
||||
* @param message The message to log
|
||||
*/
|
||||
public void warning(String message) {
|
||||
logger.warning(message);
|
||||
writeLog("[WARN] " + message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a Throwable with the provided message on WARNING level
|
||||
* and save the stack trace to the log file.
|
||||
*
|
||||
* @param message The message to accompany the exception
|
||||
* @param th The Throwable to log
|
||||
*/
|
||||
public void logException(String message, Throwable th) {
|
||||
warning(message + " " + ExceptionUtils.formatException(th));
|
||||
writeLog(Throwables.getStackTraceAsString(th));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an INFO message.
|
||||
*
|
||||
* @param message The message to log
|
||||
*/
|
||||
public void info(String message) {
|
||||
logger.info(message);
|
||||
writeLog("[INFO] " + message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a FINE message if enabled.
|
||||
* <p>
|
||||
* Implementation note: this logs a message on INFO level because
|
||||
* levels below INFO are disabled by Bukkit/Spigot.
|
||||
*
|
||||
* @param message The message to log
|
||||
*/
|
||||
public void fine(String message) {
|
||||
if (logLevel.includes(LogLevel.FINE)) {
|
||||
logger.info(message);
|
||||
writeLog("[INFO:FINE] " + message);
|
||||
}
|
||||
}
|
||||
|
||||
// --------
|
||||
// Debug log methods
|
||||
// --------
|
||||
|
||||
/**
|
||||
* Log a DEBUG message if enabled.
|
||||
* <p>
|
||||
* Implementation note: this logs a message on INFO level and prefixes it with "DEBUG" because
|
||||
* levels below INFO are disabled by Bukkit/Spigot.
|
||||
*
|
||||
* @param message The message to log
|
||||
*/
|
||||
public void debug(String message) {
|
||||
if (logLevel.includes(LogLevel.DEBUG)) {
|
||||
logAndWriteWithDebugPrefix(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the DEBUG message.
|
||||
*
|
||||
* @param message the message
|
||||
* @param param1 parameter to replace in the message
|
||||
*/
|
||||
// Avoids array creation if DEBUG level is disabled
|
||||
public void debug(String message, Object param1) {
|
||||
if (logLevel.includes(LogLevel.DEBUG)) {
|
||||
debug(message, new Object[]{param1});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the DEBUG message.
|
||||
*
|
||||
* @param message the message
|
||||
* @param param1 first param to replace in message
|
||||
* @param param2 second param to replace in message
|
||||
*/
|
||||
// Avoids array creation if DEBUG level is disabled
|
||||
public void debug(String message, Object param1, Object param2) {
|
||||
if (logLevel.includes(LogLevel.DEBUG)) {
|
||||
debug(message, new Object[]{param1, param2});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the DEBUG message.
|
||||
*
|
||||
* @param message the message
|
||||
* @param params the params to replace in the message
|
||||
*/
|
||||
public void debug(String message, Object... params) {
|
||||
if (logLevel.includes(LogLevel.DEBUG)) {
|
||||
logAndWriteWithDebugPrefix(MessageFormat.format(message, params));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the DEBUG message from the supplier if enabled.
|
||||
*
|
||||
* @param msgSupplier the message supplier
|
||||
*/
|
||||
public void debug(Supplier<String> msgSupplier) {
|
||||
if (logLevel.includes(LogLevel.DEBUG)) {
|
||||
logAndWriteWithDebugPrefix(msgSupplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
private void logAndWriteWithDebugPrefix(String message) {
|
||||
String debugMessage = "[INFO:DEBUG] " + message;
|
||||
logger.info(debugMessage);
|
||||
writeLog(debugMessage);
|
||||
}
|
||||
|
||||
// --------
|
||||
// Helpers
|
||||
// --------
|
||||
|
||||
/**
|
||||
* Closes the file writer.
|
||||
*/
|
||||
public static void closeFileWriter() {
|
||||
if (fileWriter != null) {
|
||||
try {
|
||||
fileWriter.flush();
|
||||
} catch (IOException ignored) {
|
||||
} finally {
|
||||
closeSafely(fileWriter);
|
||||
fileWriter = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a message into the log file with a TimeStamp if enabled.
|
||||
*
|
||||
* @param message The message to write to the log
|
||||
*/
|
||||
private static void writeLog(String message) {
|
||||
if (fileWriter != null) {
|
||||
String dateTime = DATE_FORMAT.format(LocalDateTime.now());
|
||||
try {
|
||||
fileWriter.write(dateTime);
|
||||
fileWriter.write(": ");
|
||||
fileWriter.write(message);
|
||||
fileWriter.write(NEW_LINE);
|
||||
fileWriter.flush();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void closeSafely(Closeable closeable) {
|
||||
if (closeable != null) {
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.SEVERE, "Failed to close resource", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the {@link #fileWriter} field if it is null, handling any exceptions that might
|
||||
* arise during its creation.
|
||||
*/
|
||||
private static void initializeFileWriter() {
|
||||
if (fileWriter == null) {
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(logFile, true);
|
||||
fileWriter = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
|
||||
} catch (Exception e) {
|
||||
closeSafely(fos);
|
||||
logger.log(Level.SEVERE, "Failed to create writer to AuthMe log file", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
370
src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java
Normal file
370
src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java
Normal file
@ -0,0 +1,370 @@
|
||||
package fr.xephi.authme.api.v3;
|
||||
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.process.register.executors.ApiPasswordRegisterParams;
|
||||
import fr.xephi.authme.process.register.executors.RegistrationMethod;
|
||||
import fr.xephi.authme.security.PasswordSecurity;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.service.GeoIpService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.util.PlayerUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The current API of AuthMe.
|
||||
*
|
||||
* Recommended method of retrieving the AuthMeApi object:
|
||||
* <code>
|
||||
* AuthMeApi authmeApi = AuthMeApi.getInstance();
|
||||
* </code>
|
||||
*/
|
||||
public class AuthMeApi {
|
||||
|
||||
private static AuthMeApi singleton;
|
||||
private final AuthMe plugin;
|
||||
private final DataSource dataSource;
|
||||
private final PasswordSecurity passwordSecurity;
|
||||
private final Management management;
|
||||
private final ValidationService validationService;
|
||||
private final PlayerCache playerCache;
|
||||
private final GeoIpService geoIpService;
|
||||
|
||||
/*
|
||||
* Constructor for AuthMeApi.
|
||||
*/
|
||||
@Inject
|
||||
AuthMeApi(AuthMe plugin, DataSource dataSource, PlayerCache playerCache, PasswordSecurity passwordSecurity,
|
||||
Management management, ValidationService validationService, GeoIpService geoIpService) {
|
||||
this.plugin = plugin;
|
||||
this.dataSource = dataSource;
|
||||
this.passwordSecurity = passwordSecurity;
|
||||
this.management = management;
|
||||
this.validationService = validationService;
|
||||
this.playerCache = playerCache;
|
||||
this.geoIpService = geoIpService;
|
||||
AuthMeApi.singleton = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the AuthMeApi object for AuthMe.
|
||||
*
|
||||
* @return The AuthMeApi object, or null if the AuthMe plugin is not enabled or not fully initialized yet
|
||||
*/
|
||||
public static AuthMeApi getInstance() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the plugin instance.
|
||||
*
|
||||
* @return The AuthMe instance
|
||||
*/
|
||||
public AuthMe getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather the version number of the plugin.
|
||||
* This can be used to determine whether certain AuthMeApi features are available or not.
|
||||
*
|
||||
* @return Plugin version identifier as a string.
|
||||
*/
|
||||
public String getPluginVersion() {
|
||||
return AuthMe.getPluginVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given player is authenticated.
|
||||
*
|
||||
* @param player The player to verify
|
||||
* @return true if the player is authenticated
|
||||
*/
|
||||
public boolean isAuthenticated(Player player) {
|
||||
return playerCache.isAuthenticated(player.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given player is an NPC.
|
||||
*
|
||||
* @param player The player to verify
|
||||
* @return true if the player is an npc
|
||||
*/
|
||||
public boolean isNpc(Player player) {
|
||||
return PlayerUtils.isNpc(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given player is unrestricted. For such players, AuthMe will not require
|
||||
* them to authenticate.
|
||||
*
|
||||
* @param player The player to verify
|
||||
* @return true if the player is unrestricted
|
||||
* @see fr.xephi.authme.settings.properties.RestrictionSettings#UNRESTRICTED_NAMES
|
||||
*/
|
||||
public boolean isUnrestricted(Player player) {
|
||||
return validationService.isUnrestricted(player.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last location of an online player.
|
||||
*
|
||||
* @param player The player to process
|
||||
* @return The location of the player
|
||||
*/
|
||||
public Location getLastLocation(Player player) {
|
||||
PlayerAuth auth = playerCache.getAuth(player.getName());
|
||||
if (auth != null) {
|
||||
return new Location(Bukkit.getWorld(auth.getWorld()),
|
||||
auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ(), auth.getYaw(), auth.getPitch());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the AuthMe info of the given player's name, or empty optional if the player doesn't exist.
|
||||
*
|
||||
* @param playerName The player name to look up
|
||||
* @return AuthMe player info, or empty optional if the player doesn't exist
|
||||
*/
|
||||
public Optional<AuthMePlayer> getPlayerInfo(String playerName) {
|
||||
PlayerAuth auth = playerCache.getAuth(playerName);
|
||||
if (auth == null) {
|
||||
auth = dataSource.getAuth(playerName);
|
||||
}
|
||||
return AuthMePlayerImpl.fromPlayerAuth(auth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last ip address of a player.
|
||||
*
|
||||
* @param playerName The name of the player to process
|
||||
* @return The last ip address of the player
|
||||
*/
|
||||
public String getLastIp(String playerName) {
|
||||
PlayerAuth auth = playerCache.getAuth(playerName);
|
||||
if (auth == null) {
|
||||
auth = dataSource.getAuth(playerName);
|
||||
}
|
||||
if (auth != null) {
|
||||
return auth.getLastIp();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user names by ip.
|
||||
*
|
||||
* @param address The ip address to process
|
||||
* @return The list of user names related to the ip address
|
||||
*/
|
||||
public List<String> getNamesByIp(String address) {
|
||||
return dataSource.getAllAuthsByIp(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last (AuthMe) login date of a player.
|
||||
*
|
||||
* @param playerName The name of the player to process
|
||||
* @return The date of the last login, or null if the player doesn't exist or has never logged in
|
||||
* @deprecated Use Java 8's Instant method {@link #getLastLoginTime(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public Date getLastLogin(String playerName) {
|
||||
Long lastLogin = getLastLoginMillis(playerName);
|
||||
return lastLogin == null ? null : new Date(lastLogin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last (AuthMe) login timestamp of a player.
|
||||
*
|
||||
* @param playerName The name of the player to process
|
||||
*
|
||||
* @return The timestamp of the last login, or null if the player doesn't exist or has never logged in
|
||||
*/
|
||||
public Instant getLastLoginTime(String playerName) {
|
||||
Long lastLogin = getLastLoginMillis(playerName);
|
||||
return lastLogin == null ? null : Instant.ofEpochMilli(lastLogin);
|
||||
}
|
||||
|
||||
private Long getLastLoginMillis(String playerName) {
|
||||
PlayerAuth auth = playerCache.getAuth(playerName);
|
||||
if (auth == null) {
|
||||
auth = dataSource.getAuth(playerName);
|
||||
}
|
||||
if (auth != null) {
|
||||
return auth.getLastLogin();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the player is registered.
|
||||
*
|
||||
* @param playerName The player name to check
|
||||
* @return true if player is registered, false otherwise
|
||||
*/
|
||||
public boolean isRegistered(String playerName) {
|
||||
String player = playerName.toLowerCase(Locale.ROOT);
|
||||
return dataSource.isAuthAvailable(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the password for the given player.
|
||||
*
|
||||
* @param playerName The player to check the password for
|
||||
* @param passwordToCheck The password to check
|
||||
* @return true if the password is correct, false otherwise
|
||||
*/
|
||||
public boolean checkPassword(String playerName, String passwordToCheck) {
|
||||
return passwordSecurity.comparePassword(passwordToCheck, playerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an OFFLINE/ONLINE player with the given password.
|
||||
*
|
||||
* @param playerName The player to register
|
||||
* @param password The password to register the player with
|
||||
*
|
||||
* @return true if the player was registered successfully
|
||||
*/
|
||||
public boolean registerPlayer(String playerName, String password) {
|
||||
String name = playerName.toLowerCase(Locale.ROOT);
|
||||
if (isRegistered(name)) {
|
||||
return false;
|
||||
}
|
||||
HashedPassword result = passwordSecurity.computeHash(password, name);
|
||||
PlayerAuth auth = PlayerAuth.builder()
|
||||
.name(name)
|
||||
.password(result)
|
||||
.realName(playerName)
|
||||
.registrationDate(System.currentTimeMillis())
|
||||
.build();
|
||||
return dataSource.saveAuth(auth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force a player to login, i.e. the player is logged in without needing his password.
|
||||
*
|
||||
* @param player The player to log in
|
||||
*/
|
||||
public void forceLogin(Player player) {
|
||||
management.forceLogin(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force a player to logout.
|
||||
*
|
||||
* @param player The player to log out
|
||||
*/
|
||||
public void forceLogout(Player player) {
|
||||
management.performLogout(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force an ONLINE player to register.
|
||||
*
|
||||
* @param player The player to register
|
||||
* @param password The password to use
|
||||
* @param autoLogin Should the player be authenticated automatically after the registration?
|
||||
*/
|
||||
public void forceRegister(Player player, String password, boolean autoLogin) {
|
||||
management.performRegister(RegistrationMethod.API_REGISTRATION,
|
||||
ApiPasswordRegisterParams.of(player, password, autoLogin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an ONLINE player with the given password.
|
||||
*
|
||||
* @param player The player to register
|
||||
* @param password The password to use
|
||||
*/
|
||||
public void forceRegister(Player player, String password) {
|
||||
forceRegister(player, password, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a player from AuthMe.
|
||||
*
|
||||
* @param player The player to unregister
|
||||
*/
|
||||
public void forceUnregister(Player player) {
|
||||
management.performUnregisterByAdmin(null, player.getName(), player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a player from AuthMe by name.
|
||||
*
|
||||
* @param name the name of the player (case-insensitive)
|
||||
*/
|
||||
public void forceUnregister(String name) {
|
||||
management.performUnregisterByAdmin(null, name, Bukkit.getPlayer(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a user's password
|
||||
*
|
||||
* @param name the user name
|
||||
* @param newPassword the new password
|
||||
*/
|
||||
public void changePassword(String name, String newPassword) {
|
||||
management.performPasswordChangeAsAdmin(null, name, newPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the registered names (lowercase)
|
||||
*
|
||||
* @return registered names
|
||||
*/
|
||||
public List<String> getRegisteredNames() {
|
||||
List<String> registeredNames = new ArrayList<>();
|
||||
dataSource.getAllAuths().forEach(auth -> registeredNames.add(auth.getNickname()));
|
||||
return registeredNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the registered real-names (original case)
|
||||
*
|
||||
* @return registered real-names
|
||||
*/
|
||||
public List<String> getRegisteredRealNames() {
|
||||
List<String> registeredNames = new ArrayList<>();
|
||||
dataSource.getAllAuths().forEach(auth -> registeredNames.add(auth.getRealName()));
|
||||
return registeredNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the country code of the given IP address.
|
||||
*
|
||||
* @param ip textual IP address to lookup.
|
||||
*
|
||||
* @return two-character ISO 3166-1 alpha code for the country.
|
||||
*/
|
||||
public String getCountryCode(String ip) {
|
||||
return geoIpService.getCountryCode(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the country name of the given IP address.
|
||||
*
|
||||
* @param ip textual IP address to lookup.
|
||||
*
|
||||
* @return The name of the country.
|
||||
*/
|
||||
public String getCountryName(String ip) {
|
||||
return geoIpService.getCountryName(ip);
|
||||
}
|
||||
}
|
||||
65
src/main/java/fr/xephi/authme/api/v3/AuthMePlayer.java
Normal file
65
src/main/java/fr/xephi/authme/api/v3/AuthMePlayer.java
Normal file
@ -0,0 +1,65 @@
|
||||
package fr.xephi.authme.api.v3;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Read-only player info exposed in the AuthMe API. The data in this object is copied from the
|
||||
* database and not updated afterwards. As such, it may become outdated if the player data changes
|
||||
* in AuthMe.
|
||||
*
|
||||
* @see AuthMeApi#getPlayerInfo
|
||||
*/
|
||||
public interface AuthMePlayer {
|
||||
|
||||
/**
|
||||
* @return the case-sensitive name of the player, e.g. "thePlayer3030" - never null
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns the UUID of the player as given by the server (may be offline UUID or not).
|
||||
* The UUID is not present if AuthMe is configured not to store the UUID or if the data is not
|
||||
* present (e.g. older record).
|
||||
*
|
||||
* @return player uuid, or empty optional if not available
|
||||
*/
|
||||
Optional<UUID> getUuid();
|
||||
|
||||
/**
|
||||
* Returns the email address associated with this player, or an empty optional if not available.
|
||||
*
|
||||
* @return player's email or empty optional
|
||||
*/
|
||||
Optional<String> getEmail();
|
||||
|
||||
/**
|
||||
* @return the registration date of the player's account - never null
|
||||
*/
|
||||
Instant getRegistrationDate();
|
||||
|
||||
/**
|
||||
* Returns the IP address with which the player's account was registered. Returns an empty optional
|
||||
* for older accounts, or if the account was registered by someone else (e.g. by an admin).
|
||||
*
|
||||
* @return the ip address used during the registration of the account, or empty optional
|
||||
*/
|
||||
Optional<String> getRegistrationIpAddress();
|
||||
|
||||
/**
|
||||
* Returns the last login date of the player. An empty optional is returned if the player never logged in.
|
||||
*
|
||||
* @return date the player last logged in successfully, or empty optional if not applicable
|
||||
*/
|
||||
Optional<Instant> getLastLoginDate();
|
||||
|
||||
/**
|
||||
* Returns the IP address the player last logged in with successfully. Returns an empty optional if the
|
||||
* player never logged in.
|
||||
*
|
||||
* @return ip address the player last logged in with successfully, or empty optional if not applicable
|
||||
*/
|
||||
Optional<String> getLastLoginIpAddress();
|
||||
|
||||
}
|
||||
93
src/main/java/fr/xephi/authme/api/v3/AuthMePlayerImpl.java
Normal file
93
src/main/java/fr/xephi/authme/api/v3/AuthMePlayerImpl.java
Normal file
@ -0,0 +1,93 @@
|
||||
package fr.xephi.authme.api.v3;
|
||||
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AuthMePlayer}. This implementation is not part of the API and
|
||||
* may have breaking changes in subsequent releases.
|
||||
*/
|
||||
class AuthMePlayerImpl implements AuthMePlayer {
|
||||
|
||||
private String name;
|
||||
private UUID uuid;
|
||||
private String email;
|
||||
|
||||
private Instant registrationDate;
|
||||
private String registrationIpAddress;
|
||||
|
||||
private Instant lastLoginDate;
|
||||
private String lastLoginIpAddress;
|
||||
|
||||
AuthMePlayerImpl() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the given player auth to an AuthMePlayer instance. Returns an empty optional if
|
||||
* the player auth is null.
|
||||
*
|
||||
* @param playerAuth the player auth or null
|
||||
* @return the mapped player auth, or empty optional if the argument was null
|
||||
*/
|
||||
static Optional<AuthMePlayer> fromPlayerAuth(PlayerAuth playerAuth) {
|
||||
if (playerAuth == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
AuthMePlayerImpl authMeUser = new AuthMePlayerImpl();
|
||||
authMeUser.name = playerAuth.getRealName();
|
||||
authMeUser.uuid = playerAuth.getUuid();
|
||||
authMeUser.email = nullIfDefault(playerAuth.getEmail(), PlayerAuth.DB_EMAIL_DEFAULT);
|
||||
Long lastLoginMillis = nullIfDefault(playerAuth.getLastLogin(), PlayerAuth.DB_LAST_LOGIN_DEFAULT);
|
||||
authMeUser.registrationDate = toInstant(playerAuth.getRegistrationDate());
|
||||
authMeUser.registrationIpAddress = playerAuth.getRegistrationIp();
|
||||
authMeUser.lastLoginDate = toInstant(lastLoginMillis);
|
||||
authMeUser.lastLoginIpAddress = nullIfDefault(playerAuth.getLastIp(), PlayerAuth.DB_LAST_IP_DEFAULT);
|
||||
return Optional.of(authMeUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Optional<UUID> getUuid() {
|
||||
return Optional.ofNullable(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getEmail() {
|
||||
return Optional.ofNullable(email);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getRegistrationDate() {
|
||||
return registrationDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getRegistrationIpAddress() {
|
||||
return Optional.ofNullable(registrationIpAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Instant> getLastLoginDate() {
|
||||
return Optional.ofNullable( lastLoginDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getLastLoginIpAddress() {
|
||||
return Optional.ofNullable(lastLoginIpAddress);
|
||||
}
|
||||
|
||||
private static Instant toInstant(Long epochMillis) {
|
||||
return epochMillis == null ? null : Instant.ofEpochMilli(epochMillis);
|
||||
}
|
||||
|
||||
private static <T> T nullIfDefault(T value, T defaultValue) {
|
||||
return defaultValue.equals(value) ? null : value;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
/**
|
||||
* Wrapper for the description of a command argument.
|
||||
*/
|
||||
public class CommandArgumentDescription {
|
||||
|
||||
/**
|
||||
* Argument name (one-word description of the argument).
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* Argument description.
|
||||
*/
|
||||
private final String description;
|
||||
/**
|
||||
* Defines whether the argument is optional.
|
||||
*/
|
||||
private final boolean isOptional;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param name The argument name.
|
||||
* @param description The argument description.
|
||||
* @param isOptional True if the argument is optional, false otherwise.
|
||||
*/
|
||||
public CommandArgumentDescription(String name, String description, boolean isOptional) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.isOptional = isOptional;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the argument name.
|
||||
*
|
||||
* @return Argument name.
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the argument description.
|
||||
*
|
||||
* @return Argument description.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the argument is optional.
|
||||
*
|
||||
* @return True if the argument is optional, false otherwise.
|
||||
*/
|
||||
public boolean isOptional() {
|
||||
return isOptional;
|
||||
}
|
||||
|
||||
}
|
||||
294
src/main/java/fr/xephi/authme/command/CommandDescription.java
Normal file
294
src/main/java/fr/xephi/authme/command/CommandDescription.java
Normal file
@ -0,0 +1,294 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* Command description – defines which labels ("names") will lead to a command and points to the
|
||||
* {@link ExecutableCommand} implementation that executes the logic of the command.
|
||||
* <p>
|
||||
* CommandDescription instances are built hierarchically: they have one parent, or {@code null} for base commands
|
||||
* (main commands such as {@code /authme}), and may have multiple children extending the mapping of the parent: e.g. if
|
||||
* {@code /authme} has a child whose label is {@code "register"}, then {@code /authme register} is the command that
|
||||
* the child defines.
|
||||
*/
|
||||
@SuppressWarnings("checkstyle:FinalClass") // Justification: class is mocked in multiple tests
|
||||
public class CommandDescription {
|
||||
|
||||
/**
|
||||
* Defines the labels to execute the command. For example, if labels are "register" and "r" and the parent is
|
||||
* the command for "/authme", then both "/authme register" and "/authme r" will be handled by this command.
|
||||
*/
|
||||
private List<String> labels;
|
||||
/**
|
||||
* Short description of the command.
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* Detailed description of what the command does.
|
||||
*/
|
||||
private String detailedDescription;
|
||||
/**
|
||||
* The class implementing the command described by this object.
|
||||
*/
|
||||
private Class<? extends ExecutableCommand> executableCommand;
|
||||
/**
|
||||
* The parent command.
|
||||
*/
|
||||
private CommandDescription parent;
|
||||
/**
|
||||
* The child commands that extend this command.
|
||||
*/
|
||||
private List<CommandDescription> children = new ArrayList<>();
|
||||
/**
|
||||
* The arguments the command takes.
|
||||
*/
|
||||
private List<CommandArgumentDescription> arguments;
|
||||
/**
|
||||
* Permission node required to execute this command.
|
||||
*/
|
||||
private PermissionNode permission;
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
* <p>
|
||||
* Note for developers: Instances should be created with {@link CommandBuilder#register()} to be properly
|
||||
* registered in the command tree.
|
||||
*
|
||||
* @param labels command labels
|
||||
* @param description description of the command
|
||||
* @param detailedDescription detailed command description
|
||||
* @param executableCommand class of the command implementation
|
||||
* @param parent parent command
|
||||
* @param arguments command arguments
|
||||
* @param permission permission node required to execute this command
|
||||
*/
|
||||
private CommandDescription(List<String> labels, String description, String detailedDescription,
|
||||
Class<? extends ExecutableCommand> executableCommand, CommandDescription parent,
|
||||
List<CommandArgumentDescription> arguments, PermissionNode permission) {
|
||||
this.labels = labels;
|
||||
this.description = description;
|
||||
this.detailedDescription = detailedDescription;
|
||||
this.executableCommand = executableCommand;
|
||||
this.parent = parent;
|
||||
this.arguments = arguments;
|
||||
this.permission = permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all relative labels of this command. For example, if this object describes {@code /authme register} and
|
||||
* {@code /authme r}, then it will return a list with {@code register} and {@code r}. The parent label
|
||||
* {@code authme} is not returned.
|
||||
*
|
||||
* @return All labels of the command description.
|
||||
*/
|
||||
public List<String> getLabels() {
|
||||
return labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this command description has the given label.
|
||||
*
|
||||
* @param commandLabel The label to check for.
|
||||
*
|
||||
* @return {@code true} if this command contains the given label, {@code false} otherwise.
|
||||
*/
|
||||
public boolean hasLabel(String commandLabel) {
|
||||
for (String label : labels) {
|
||||
if (label.equalsIgnoreCase(commandLabel)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link ExecutableCommand} class implementing this command.
|
||||
*
|
||||
* @return The executable command class
|
||||
*/
|
||||
public Class<? extends ExecutableCommand> getExecutableCommand() {
|
||||
return executableCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parent.
|
||||
*
|
||||
* @return The parent command, or null for base commands
|
||||
*/
|
||||
public CommandDescription getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of labels necessary to get to this command. This corresponds to the number of parents + 1.
|
||||
*
|
||||
* @return The number of labels, e.g. for "/authme abc def" the label count is 3
|
||||
*/
|
||||
public int getLabelCount() {
|
||||
if (parent == null) {
|
||||
return 1;
|
||||
}
|
||||
return parent.getLabelCount() + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all command children.
|
||||
*
|
||||
* @return Command children.
|
||||
*/
|
||||
public List<CommandDescription> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all arguments the command takes.
|
||||
*
|
||||
* @return Command arguments.
|
||||
*/
|
||||
public List<CommandArgumentDescription> getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a short description of the command.
|
||||
*
|
||||
* @return Command description.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a detailed description of the command.
|
||||
*
|
||||
* @return Detailed description.
|
||||
*/
|
||||
public String getDetailedDescription() {
|
||||
return detailedDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the permission node required to execute the command.
|
||||
*
|
||||
* @return The permission node, or null if none are required to execute the command.
|
||||
*/
|
||||
public PermissionNode getPermission() {
|
||||
return permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a builder instance to create a new command description.
|
||||
*
|
||||
* @return The builder
|
||||
*/
|
||||
public static CommandBuilder builder() {
|
||||
return new CommandBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for initializing CommandDescription objects.
|
||||
*/
|
||||
public static final class CommandBuilder {
|
||||
private List<String> labels;
|
||||
private String description;
|
||||
private String detailedDescription;
|
||||
private Class<? extends ExecutableCommand> executableCommand;
|
||||
private CommandDescription parent;
|
||||
private List<CommandArgumentDescription> arguments = new ArrayList<>();
|
||||
private PermissionNode permission;
|
||||
|
||||
/**
|
||||
* Build a CommandDescription and register it onto the parent if available.
|
||||
*
|
||||
* @return The generated CommandDescription object
|
||||
*/
|
||||
public CommandDescription register() {
|
||||
CommandDescription command = build();
|
||||
|
||||
if (command.parent != null) {
|
||||
command.parent.children.add(command);
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a CommandDescription (without registering it on the parent).
|
||||
*
|
||||
* @return The generated CommandDescription object
|
||||
*/
|
||||
public CommandDescription build() {
|
||||
checkArgument(!Utils.isCollectionEmpty(labels), "Labels may not be empty");
|
||||
checkArgument(!StringUtils.isBlank(description), "Description may not be empty");
|
||||
checkArgument(!StringUtils.isBlank(detailedDescription), "Detailed description may not be empty");
|
||||
checkArgument(executableCommand != null, "Executable command must be set");
|
||||
// parents and permissions may be null; arguments may be empty
|
||||
|
||||
return new CommandDescription(labels, description, detailedDescription, executableCommand,
|
||||
parent, arguments, permission);
|
||||
}
|
||||
|
||||
public CommandBuilder labels(List<String> labels) {
|
||||
this.labels = labels;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder labels(String... labels) {
|
||||
return labels(asList(labels));
|
||||
}
|
||||
|
||||
public CommandBuilder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder detailedDescription(String detailedDescription) {
|
||||
this.detailedDescription = detailedDescription;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder executableCommand(Class<? extends ExecutableCommand> executableCommand) {
|
||||
this.executableCommand = executableCommand;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder parent(CommandDescription parent) {
|
||||
this.parent = parent;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an argument that the command description requires. This method can be called multiples times to add
|
||||
* multiple arguments.
|
||||
*
|
||||
* @param label The label of the argument (single word name of the argument)
|
||||
* @param description The description of the argument
|
||||
* @param isOptional True if the argument is optional, false if it is mandatory
|
||||
*
|
||||
* @return The builder
|
||||
*/
|
||||
public CommandBuilder withArgument(String label, String description, boolean isOptional) {
|
||||
arguments.add(new CommandArgumentDescription(label, description, isOptional));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a permission node that a user must have to execute the command.
|
||||
*
|
||||
* @param permission The PermissionNode to add
|
||||
* @return The builder
|
||||
*/
|
||||
public CommandBuilder permission(PermissionNode permission) {
|
||||
this.permission = permission;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
186
src/main/java/fr/xephi/authme/command/CommandHandler.java
Normal file
186
src/main/java/fr/xephi/authme/command/CommandHandler.java
Normal file
@ -0,0 +1,186 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.command.help.HelpProvider;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The AuthMe command handler, responsible for invoking the correct {@link ExecutableCommand} based on incoming
|
||||
* command labels or for displaying a help message for unknown command labels.
|
||||
*/
|
||||
public class CommandHandler {
|
||||
|
||||
/**
|
||||
* The threshold for suggesting a similar command. If the difference is below this value, we will
|
||||
* ask the player whether he meant the similar command.
|
||||
*/
|
||||
private static final double SUGGEST_COMMAND_THRESHOLD = 0.75;
|
||||
|
||||
private final CommandMapper commandMapper;
|
||||
private final PermissionsManager permissionsManager;
|
||||
private final Messages messages;
|
||||
private final HelpProvider helpProvider;
|
||||
|
||||
/**
|
||||
* Map with ExecutableCommand children. The key is the type of the value.
|
||||
*/
|
||||
private Map<Class<? extends ExecutableCommand>, ExecutableCommand> commands = new HashMap<>();
|
||||
|
||||
@Inject
|
||||
CommandHandler(Factory<ExecutableCommand> commandFactory, CommandMapper commandMapper,
|
||||
PermissionsManager permissionsManager, Messages messages, HelpProvider helpProvider) {
|
||||
this.commandMapper = commandMapper;
|
||||
this.permissionsManager = permissionsManager;
|
||||
this.messages = messages;
|
||||
this.helpProvider = helpProvider;
|
||||
initializeCommands(commandFactory, commandMapper.getCommandClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a command that was invoked to the proper {@link CommandDescription} or return a useful error
|
||||
* message upon failure.
|
||||
*
|
||||
* @param sender The command sender.
|
||||
* @param bukkitCommandLabel The command label (Bukkit).
|
||||
* @param bukkitArgs The command arguments (Bukkit).
|
||||
*
|
||||
* @return True if the command was executed, false otherwise.
|
||||
*/
|
||||
public boolean processCommand(CommandSender sender, String bukkitCommandLabel, String[] bukkitArgs) {
|
||||
// Add the Bukkit command label to the front so we get a list like [authme, register, bobby, mysecret]
|
||||
List<String> parts = skipEmptyArguments(bukkitArgs);
|
||||
parts.add(0, bukkitCommandLabel);
|
||||
|
||||
FoundCommandResult result = commandMapper.mapPartsToCommand(sender, parts);
|
||||
handleCommandResult(sender, result);
|
||||
return !FoundResultStatus.MISSING_BASE_COMMAND.equals(result.getResultStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the given {@link FoundCommandResult} for the provided command sender.
|
||||
*
|
||||
* @param sender the command sender who executed the command
|
||||
* @param result the command mapping result
|
||||
*/
|
||||
private void handleCommandResult(CommandSender sender, FoundCommandResult result) {
|
||||
switch (result.getResultStatus()) {
|
||||
case SUCCESS:
|
||||
executeCommand(sender, result);
|
||||
break;
|
||||
case MISSING_BASE_COMMAND:
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Failed to parse " + AuthMe.getPluginName() + " command!");
|
||||
break;
|
||||
case INCORRECT_ARGUMENTS:
|
||||
sendImproperArgumentsMessage(sender, result);
|
||||
break;
|
||||
case UNKNOWN_LABEL:
|
||||
sendUnknownCommandMessage(sender, result);
|
||||
break;
|
||||
case NO_PERMISSION:
|
||||
messages.send(sender, MessageKey.NO_PERMISSION);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown result status '" + result.getResultStatus() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all required ExecutableCommand objects.
|
||||
*
|
||||
* @param commandFactory factory to create command objects
|
||||
* @param commandClasses the classes to instantiate
|
||||
*/
|
||||
private void initializeCommands(Factory<ExecutableCommand> commandFactory,
|
||||
Set<Class<? extends ExecutableCommand>> commandClasses) {
|
||||
for (Class<? extends ExecutableCommand> clazz : commandClasses) {
|
||||
commands.put(clazz, commandFactory.newInstance(clazz));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command for the given command sender.
|
||||
*
|
||||
* @param sender The sender which initiated the command
|
||||
* @param result The mapped result
|
||||
*/
|
||||
private void executeCommand(CommandSender sender, FoundCommandResult result) {
|
||||
ExecutableCommand executableCommand = commands.get(result.getCommandDescription().getExecutableCommand());
|
||||
List<String> arguments = result.getArguments();
|
||||
executableCommand.executeCommand(sender, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip all entries of the given array that are simply whitespace.
|
||||
*
|
||||
* @param args The array to process
|
||||
* @return List of the items that are not empty
|
||||
*/
|
||||
private static List<String> skipEmptyArguments(String[] args) {
|
||||
List<String> cleanArguments = new ArrayList<>();
|
||||
for (String argument : args) {
|
||||
if (!StringUtils.isBlank(argument)) {
|
||||
cleanArguments.add(argument);
|
||||
}
|
||||
}
|
||||
return cleanArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an "unknown command" message to the user and suggest an existing command if its similarity is within
|
||||
* the defined threshold.
|
||||
*
|
||||
* @param sender The command sender
|
||||
* @param result The command that was found during the mapping process
|
||||
*/
|
||||
private static void sendUnknownCommandMessage(CommandSender sender, FoundCommandResult result) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Unknown command!");
|
||||
|
||||
// Show a command suggestion if available and the difference isn't too big
|
||||
if (result.getDifference() <= SUGGEST_COMMAND_THRESHOLD && result.getCommandDescription() != null) {
|
||||
sender.sendMessage(ChatColor.YELLOW + "Did you mean " + ChatColor.GOLD
|
||||
+ CommandUtils.constructCommandPath(result.getCommandDescription()) + ChatColor.YELLOW + "?");
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.YELLOW + "Use the command " + ChatColor.GOLD + "/" + result.getLabels().get(0)
|
||||
+ " help" + ChatColor.YELLOW + " to view help.");
|
||||
}
|
||||
|
||||
private void sendImproperArgumentsMessage(CommandSender sender, FoundCommandResult result) {
|
||||
CommandDescription command = result.getCommandDescription();
|
||||
if (!permissionsManager.hasPermission(sender, command.getPermission())) {
|
||||
messages.send(sender, MessageKey.NO_PERMISSION);
|
||||
return;
|
||||
}
|
||||
|
||||
ExecutableCommand executableCommand = commands.get(command.getExecutableCommand());
|
||||
MessageKey usageMessage = executableCommand.getArgumentsMismatchMessage();
|
||||
if (usageMessage == null) {
|
||||
showHelpForCommand(sender, result);
|
||||
} else {
|
||||
messages.send(sender, usageMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void showHelpForCommand(CommandSender sender, FoundCommandResult result) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Incorrect command arguments!");
|
||||
helpProvider.outputHelp(sender, result, HelpProvider.SHOW_ARGUMENTS);
|
||||
|
||||
List<String> labels = result.getLabels();
|
||||
String childLabel = labels.size() >= 2 ? labels.get(1) : "";
|
||||
sender.sendMessage(ChatColor.GOLD + "Detailed help: " + ChatColor.WHITE
|
||||
+ "/" + labels.get(0) + " help " + childLabel);
|
||||
}
|
||||
}
|
||||
652
src/main/java/fr/xephi/authme/command/CommandInitializer.java
Normal file
652
src/main/java/fr/xephi/authme/command/CommandInitializer.java
Normal file
@ -0,0 +1,652 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import fr.xephi.authme.command.executable.HelpCommand;
|
||||
import fr.xephi.authme.command.executable.authme.AccountsCommand;
|
||||
import fr.xephi.authme.command.executable.authme.AuthMeCommand;
|
||||
import fr.xephi.authme.command.executable.authme.BackupCommand;
|
||||
import fr.xephi.authme.command.executable.authme.ChangePasswordAdminCommand;
|
||||
import fr.xephi.authme.command.executable.authme.ConverterCommand;
|
||||
import fr.xephi.authme.command.executable.authme.FirstSpawnCommand;
|
||||
import fr.xephi.authme.command.executable.authme.ForceLoginCommand;
|
||||
import fr.xephi.authme.command.executable.authme.GetEmailCommand;
|
||||
import fr.xephi.authme.command.executable.authme.GetIpCommand;
|
||||
import fr.xephi.authme.command.executable.authme.LastLoginCommand;
|
||||
import fr.xephi.authme.command.executable.authme.PurgeBannedPlayersCommand;
|
||||
import fr.xephi.authme.command.executable.authme.PurgeCommand;
|
||||
import fr.xephi.authme.command.executable.authme.PurgeLastPositionCommand;
|
||||
import fr.xephi.authme.command.executable.authme.PurgePlayerCommand;
|
||||
import fr.xephi.authme.command.executable.authme.RecentPlayersCommand;
|
||||
import fr.xephi.authme.command.executable.authme.RegisterAdminCommand;
|
||||
import fr.xephi.authme.command.executable.authme.ReloadCommand;
|
||||
import fr.xephi.authme.command.executable.authme.SetEmailCommand;
|
||||
import fr.xephi.authme.command.executable.authme.SetFirstSpawnCommand;
|
||||
import fr.xephi.authme.command.executable.authme.SetSpawnCommand;
|
||||
import fr.xephi.authme.command.executable.authme.SpawnCommand;
|
||||
import fr.xephi.authme.command.executable.authme.SwitchAntiBotCommand;
|
||||
import fr.xephi.authme.command.executable.authme.TotpDisableAdminCommand;
|
||||
import fr.xephi.authme.command.executable.authme.TotpViewStatusCommand;
|
||||
import fr.xephi.authme.command.executable.authme.UnregisterAdminCommand;
|
||||
import fr.xephi.authme.command.executable.authme.UpdateHelpMessagesCommand;
|
||||
import fr.xephi.authme.command.executable.authme.VersionCommand;
|
||||
import fr.xephi.authme.command.executable.authme.debug.DebugCommand;
|
||||
import fr.xephi.authme.command.executable.captcha.CaptchaCommand;
|
||||
import fr.xephi.authme.command.executable.changepassword.ChangePasswordCommand;
|
||||
import fr.xephi.authme.command.executable.email.AddEmailCommand;
|
||||
import fr.xephi.authme.command.executable.email.ChangeEmailCommand;
|
||||
import fr.xephi.authme.command.executable.email.EmailBaseCommand;
|
||||
import fr.xephi.authme.command.executable.email.EmailSetPasswordCommand;
|
||||
import fr.xephi.authme.command.executable.email.ProcessCodeCommand;
|
||||
import fr.xephi.authme.command.executable.email.RecoverEmailCommand;
|
||||
import fr.xephi.authme.command.executable.email.ShowEmailCommand;
|
||||
import fr.xephi.authme.command.executable.login.LoginCommand;
|
||||
import fr.xephi.authme.command.executable.logout.LogoutCommand;
|
||||
import fr.xephi.authme.command.executable.register.RegisterCommand;
|
||||
import fr.xephi.authme.command.executable.totp.AddTotpCommand;
|
||||
import fr.xephi.authme.command.executable.totp.ConfirmTotpCommand;
|
||||
import fr.xephi.authme.command.executable.totp.RemoveTotpCommand;
|
||||
import fr.xephi.authme.command.executable.totp.TotpBaseCommand;
|
||||
import fr.xephi.authme.command.executable.totp.TotpCodeCommand;
|
||||
import fr.xephi.authme.command.executable.unregister.UnregisterCommand;
|
||||
import fr.xephi.authme.command.executable.verification.VerificationCommand;
|
||||
import fr.xephi.authme.permission.AdminPermission;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PlayerPermission;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Initializes all available AuthMe commands.
|
||||
*/
|
||||
public class CommandInitializer {
|
||||
|
||||
private static final boolean OPTIONAL = true;
|
||||
private static final boolean MANDATORY = false;
|
||||
|
||||
private List<CommandDescription> commands;
|
||||
|
||||
public CommandInitializer() {
|
||||
buildCommands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of all AuthMe commands.
|
||||
*
|
||||
* @return the command descriptions
|
||||
*/
|
||||
public List<CommandDescription> getCommands() {
|
||||
return commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the command description objects for all available AuthMe commands.
|
||||
*/
|
||||
private void buildCommands() {
|
||||
// Register /authme and /email commands
|
||||
CommandDescription authMeBase = buildAuthMeBaseCommand();
|
||||
CommandDescription emailBase = buildEmailBaseCommand();
|
||||
|
||||
// Register the base login command
|
||||
CommandDescription loginBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("login", "l", "log")
|
||||
.description("Login command")
|
||||
.detailedDescription("Command to log in using AuthMeReloaded.")
|
||||
.withArgument("password", "Login password", MANDATORY)
|
||||
.permission(PlayerPermission.LOGIN)
|
||||
.executableCommand(LoginCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base logout command
|
||||
CommandDescription logoutBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("logout")
|
||||
.description("Logout command")
|
||||
.detailedDescription("Command to logout using AuthMeReloaded.")
|
||||
.permission(PlayerPermission.LOGOUT)
|
||||
.executableCommand(LogoutCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base register command
|
||||
CommandDescription registerBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("register", "reg")
|
||||
.description("Register an account")
|
||||
.detailedDescription("Command to register using AuthMeReloaded.")
|
||||
.withArgument("password", "Password", OPTIONAL)
|
||||
.withArgument("verifyPassword", "Verify password", OPTIONAL)
|
||||
.permission(PlayerPermission.REGISTER)
|
||||
.executableCommand(RegisterCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base unregister command
|
||||
CommandDescription unregisterBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("unregister", "unreg")
|
||||
.description("Unregister an account")
|
||||
.detailedDescription("Command to unregister using AuthMeReloaded.")
|
||||
.withArgument("password", "Password", MANDATORY)
|
||||
.permission(PlayerPermission.UNREGISTER)
|
||||
.executableCommand(UnregisterCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base changepassword command
|
||||
CommandDescription changePasswordBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("changepassword", "changepass", "cp")
|
||||
.description("Change password of an account")
|
||||
.detailedDescription("Command to change your password using AuthMeReloaded.")
|
||||
.withArgument("oldPassword", "Old password", MANDATORY)
|
||||
.withArgument("newPassword", "New password", MANDATORY)
|
||||
.permission(PlayerPermission.CHANGE_PASSWORD)
|
||||
.executableCommand(ChangePasswordCommand.class)
|
||||
.register();
|
||||
|
||||
// Create totp base command
|
||||
CommandDescription totpBase = buildTotpBaseCommand();
|
||||
|
||||
// Register the base captcha command
|
||||
CommandDescription captchaBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("captcha")
|
||||
.description("Captcha command")
|
||||
.detailedDescription("Captcha command for AuthMeReloaded.")
|
||||
.withArgument("captcha", "The Captcha", MANDATORY)
|
||||
.permission(PlayerPermission.CAPTCHA)
|
||||
.executableCommand(CaptchaCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base verification code command
|
||||
CommandDescription verificationBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("verification")
|
||||
.description("Verification command")
|
||||
.detailedDescription("Command to complete the verification process for AuthMeReloaded.")
|
||||
.withArgument("code", "The code", MANDATORY)
|
||||
.permission(PlayerPermission.VERIFICATION_CODE)
|
||||
.executableCommand(VerificationCommand.class)
|
||||
.register();
|
||||
|
||||
List<CommandDescription> baseCommands = ImmutableList.of(authMeBase, emailBase, loginBase, logoutBase,
|
||||
registerBase, unregisterBase, changePasswordBase, totpBase, captchaBase, verificationBase);
|
||||
|
||||
setHelpOnAllBases(baseCommands);
|
||||
commands = baseCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command description object for {@code /authme} including its children.
|
||||
*
|
||||
* @return the authme base command description
|
||||
*/
|
||||
private CommandDescription buildAuthMeBaseCommand() {
|
||||
// Register the base AuthMe Reloaded command
|
||||
CommandDescription authmeBase = CommandDescription.builder()
|
||||
.labels("authme")
|
||||
.description("AuthMe op commands")
|
||||
.detailedDescription("The main AuthMeReloaded command. The root for all admin commands.")
|
||||
.executableCommand(AuthMeCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the register command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("register", "reg", "r")
|
||||
.description("Register a player")
|
||||
.detailedDescription("Register the specified player with the specified password.")
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.withArgument("password", "Password", MANDATORY)
|
||||
.permission(AdminPermission.REGISTER)
|
||||
.executableCommand(RegisterAdminCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the unregister command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("unregister", "unreg", "unr")
|
||||
.description("Unregister a player")
|
||||
.detailedDescription("Unregister the specified player.")
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.permission(AdminPermission.UNREGISTER)
|
||||
.executableCommand(UnregisterAdminCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the forcelogin command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("forcelogin", "login")
|
||||
.description("Enforce login player")
|
||||
.detailedDescription("Enforce the specified player to login.")
|
||||
.withArgument("player", "Online player name", OPTIONAL)
|
||||
.permission(AdminPermission.FORCE_LOGIN)
|
||||
.executableCommand(ForceLoginCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the changepassword command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("password", "changepassword", "changepass", "cp")
|
||||
.description("Change a player's password")
|
||||
.detailedDescription("Change the password of a player.")
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.withArgument("pwd", "New password", MANDATORY)
|
||||
.permission(AdminPermission.CHANGE_PASSWORD)
|
||||
.executableCommand(ChangePasswordAdminCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the last login command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("lastlogin", "ll")
|
||||
.description("Player's last login")
|
||||
.detailedDescription("View the date of the specified players last login.")
|
||||
.withArgument("player", "Player name", OPTIONAL)
|
||||
.permission(AdminPermission.LAST_LOGIN)
|
||||
.executableCommand(LastLoginCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the accounts command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("accounts", "account")
|
||||
.description("Display player accounts")
|
||||
.detailedDescription("Display all accounts of a player by his player name or IP.")
|
||||
.withArgument("player", "Player name or IP", OPTIONAL)
|
||||
.permission(AdminPermission.ACCOUNTS)
|
||||
.executableCommand(AccountsCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the getemail command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("email", "mail", "getemail", "getmail")
|
||||
.description("Display player's email")
|
||||
.detailedDescription("Display the email address of the specified player if set.")
|
||||
.withArgument("player", "Player name", OPTIONAL)
|
||||
.permission(AdminPermission.GET_EMAIL)
|
||||
.executableCommand(GetEmailCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the setemail command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("setemail", "setmail", "chgemail", "chgmail")
|
||||
.description("Change player's email")
|
||||
.detailedDescription("Change the email address of the specified player.")
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.withArgument("email", "Player email", MANDATORY)
|
||||
.permission(AdminPermission.CHANGE_EMAIL)
|
||||
.executableCommand(SetEmailCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the getip command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("getip", "ip")
|
||||
.description("Get player's IP")
|
||||
.detailedDescription("Get the IP address of the specified online player.")
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.permission(AdminPermission.GET_IP)
|
||||
.executableCommand(GetIpCommand.class)
|
||||
.register();
|
||||
|
||||
// Register totp command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("totp", "2fa")
|
||||
.description("See if a player has enabled TOTP")
|
||||
.detailedDescription("Returns whether the specified player has enabled two-factor authentication.")
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.permission(AdminPermission.VIEW_TOTP_STATUS)
|
||||
.executableCommand(TotpViewStatusCommand.class)
|
||||
.register();
|
||||
|
||||
// Register disable totp command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("disabletotp", "disable2fa", "deletetotp", "delete2fa")
|
||||
.description("Delete TOTP token of a player")
|
||||
.detailedDescription("Disable two-factor authentication for a player.")
|
||||
.withArgument("player", "Player name", MANDATORY)
|
||||
.permission(AdminPermission.DISABLE_TOTP)
|
||||
.executableCommand(TotpDisableAdminCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the spawn command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("spawn", "home")
|
||||
.description("Teleport to spawn")
|
||||
.detailedDescription("Teleport to the spawn.")
|
||||
.permission(AdminPermission.SPAWN)
|
||||
.executableCommand(SpawnCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the setspawn command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("setspawn", "chgspawn")
|
||||
.description("Change the spawn")
|
||||
.detailedDescription("Change the player's spawn to your current position.")
|
||||
.permission(AdminPermission.SET_SPAWN)
|
||||
.executableCommand(SetSpawnCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the firstspawn command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("firstspawn", "firsthome")
|
||||
.description("Teleport to first spawn")
|
||||
.detailedDescription("Teleport to the first spawn.")
|
||||
.permission(AdminPermission.FIRST_SPAWN)
|
||||
.executableCommand(FirstSpawnCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the setfirstspawn command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("setfirstspawn", "chgfirstspawn")
|
||||
.description("Change the first spawn")
|
||||
.detailedDescription("Change the first player's spawn to your current position.")
|
||||
.permission(AdminPermission.SET_FIRST_SPAWN)
|
||||
.executableCommand(SetFirstSpawnCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the purge command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("purge", "delete")
|
||||
.description("Purge old data")
|
||||
.detailedDescription("Purge old AuthMeReloaded data longer than the specified number of days ago.")
|
||||
.withArgument("days", "Number of days", MANDATORY)
|
||||
.permission(AdminPermission.PURGE)
|
||||
.executableCommand(PurgeCommand.class)
|
||||
.register();
|
||||
|
||||
// Purge player command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("purgeplayer")
|
||||
.description("Purges the data of one player")
|
||||
.detailedDescription("Purges data of the given player.")
|
||||
.withArgument("player", "The player to purge", MANDATORY)
|
||||
.withArgument("options", "'force' to run without checking if player is registered", OPTIONAL)
|
||||
.permission(AdminPermission.PURGE_PLAYER)
|
||||
.executableCommand(PurgePlayerCommand.class)
|
||||
.register();
|
||||
|
||||
// Backup command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("backup")
|
||||
.description("Perform a backup")
|
||||
.detailedDescription("Creates a backup of the registered users.")
|
||||
.permission(AdminPermission.BACKUP)
|
||||
.executableCommand(BackupCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the purgelastposition command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("resetpos", "purgelastposition", "purgelastpos", "resetposition",
|
||||
"resetlastposition", "resetlastpos")
|
||||
.description("Purge player's last position")
|
||||
.detailedDescription("Purge the last know position of the specified player or all of them.")
|
||||
.withArgument("player/*", "Player name or * for all players", MANDATORY)
|
||||
.permission(AdminPermission.PURGE_LAST_POSITION)
|
||||
.executableCommand(PurgeLastPositionCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the purgebannedplayers command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("purgebannedplayers", "purgebannedplayer", "deletebannedplayers", "deletebannedplayer")
|
||||
.description("Purge banned players data")
|
||||
.detailedDescription("Purge all AuthMeReloaded data for banned players.")
|
||||
.permission(AdminPermission.PURGE_BANNED_PLAYERS)
|
||||
.executableCommand(PurgeBannedPlayersCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the switchantibot command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("switchantibot", "toggleantibot", "antibot")
|
||||
.description("Switch AntiBot mode")
|
||||
.detailedDescription("Switch or toggle the AntiBot mode to the specified state.")
|
||||
.withArgument("mode", "ON / OFF", OPTIONAL)
|
||||
.permission(AdminPermission.SWITCH_ANTIBOT)
|
||||
.executableCommand(SwitchAntiBotCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the reload command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("reload", "rld")
|
||||
.description("Reload plugin")
|
||||
.detailedDescription("Reload the AuthMeReloaded plugin.")
|
||||
.permission(AdminPermission.RELOAD)
|
||||
.executableCommand(ReloadCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the version command
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("version", "ver", "v", "about", "info")
|
||||
.description("Version info")
|
||||
.detailedDescription("Show detailed information about the installed AuthMeReloaded version, the "
|
||||
+ "developers, contributors, and license.")
|
||||
.executableCommand(VersionCommand.class)
|
||||
.register();
|
||||
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("converter", "convert", "conv")
|
||||
.description("Converter command")
|
||||
.detailedDescription("Converter command for AuthMeReloaded.")
|
||||
.withArgument("job", "Conversion job: xauth / crazylogin / rakamak / "
|
||||
+ "royalauth / vauth / sqliteToSql / mysqlToSqlite / loginsecurity", OPTIONAL)
|
||||
.permission(AdminPermission.CONVERTER)
|
||||
.executableCommand(ConverterCommand.class)
|
||||
.register();
|
||||
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("messages", "msg")
|
||||
.description("Add missing help messages")
|
||||
.detailedDescription("Adds missing texts to the current help messages file.")
|
||||
.permission(AdminPermission.UPDATE_MESSAGES)
|
||||
.executableCommand(UpdateHelpMessagesCommand.class)
|
||||
.register();
|
||||
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("recent")
|
||||
.description("See players who have recently logged in")
|
||||
.detailedDescription("Shows the last players that have logged in.")
|
||||
.permission(AdminPermission.SEE_RECENT_PLAYERS)
|
||||
.executableCommand(RecentPlayersCommand.class)
|
||||
.register();
|
||||
|
||||
CommandDescription.builder()
|
||||
.parent(authmeBase)
|
||||
.labels("debug", "dbg")
|
||||
.description("Debug features")
|
||||
.detailedDescription("Allows various operations for debugging.")
|
||||
.withArgument("child", "The child to execute", OPTIONAL)
|
||||
.withArgument("arg", "argument (depends on debug section)", OPTIONAL)
|
||||
.withArgument("arg", "argument (depends on debug section)", OPTIONAL)
|
||||
.permission(DebugSectionPermissions.DEBUG_COMMAND)
|
||||
.executableCommand(DebugCommand.class)
|
||||
.register();
|
||||
|
||||
return authmeBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command description for {@code /email} including its children.
|
||||
*
|
||||
* @return the email base command description
|
||||
*/
|
||||
private CommandDescription buildEmailBaseCommand() {
|
||||
// Register the base Email command
|
||||
CommandDescription emailBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("email")
|
||||
.description("Add email or recover password")
|
||||
.detailedDescription("The AuthMeReloaded email command base.")
|
||||
.executableCommand(EmailBaseCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the show command
|
||||
CommandDescription.builder()
|
||||
.parent(emailBase)
|
||||
.labels("show", "myemail")
|
||||
.description("Show Email")
|
||||
.detailedDescription("Show your current email address.")
|
||||
.permission(PlayerPermission.SEE_EMAIL)
|
||||
.executableCommand(ShowEmailCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the add command
|
||||
CommandDescription.builder()
|
||||
.parent(emailBase)
|
||||
.labels("add", "addemail", "addmail")
|
||||
.description("Add Email")
|
||||
.detailedDescription("Add a new email address to your account.")
|
||||
.withArgument("email", "Email address", MANDATORY)
|
||||
.withArgument("verifyEmail", "Email address verification", MANDATORY)
|
||||
.permission(PlayerPermission.ADD_EMAIL)
|
||||
.executableCommand(AddEmailCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the change command
|
||||
CommandDescription.builder()
|
||||
.parent(emailBase)
|
||||
.labels("change", "changeemail", "changemail")
|
||||
.description("Change Email")
|
||||
.detailedDescription("Change an email address of your account.")
|
||||
.withArgument("oldEmail", "Old email address", MANDATORY)
|
||||
.withArgument("newEmail", "New email address", MANDATORY)
|
||||
.permission(PlayerPermission.CHANGE_EMAIL)
|
||||
.executableCommand(ChangeEmailCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the recover command
|
||||
CommandDescription.builder()
|
||||
.parent(emailBase)
|
||||
.labels("recover", "recovery", "recoveremail", "recovermail")
|
||||
.description("Recover password using email")
|
||||
.detailedDescription("Recover your account using an Email address by sending a mail containing "
|
||||
+ "a new password.")
|
||||
.withArgument("email", "Email address", MANDATORY)
|
||||
.permission(PlayerPermission.RECOVER_EMAIL)
|
||||
.executableCommand(RecoverEmailCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the process recovery code command
|
||||
CommandDescription.builder()
|
||||
.parent(emailBase)
|
||||
.labels("code")
|
||||
.description("Submit code to recover password")
|
||||
.detailedDescription("Recover your account by submitting a code delivered to your email.")
|
||||
.withArgument("code", "Recovery code", MANDATORY)
|
||||
.permission(PlayerPermission.RECOVER_EMAIL)
|
||||
.executableCommand(ProcessCodeCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the change password after recovery command
|
||||
CommandDescription.builder()
|
||||
.parent(emailBase)
|
||||
.labels("setpassword")
|
||||
.description("Set new password after recovery")
|
||||
.detailedDescription("Set a new password after successfully recovering your account.")
|
||||
.withArgument("password", "New password", MANDATORY)
|
||||
.permission(PlayerPermission.RECOVER_EMAIL)
|
||||
.executableCommand(EmailSetPasswordCommand.class)
|
||||
.register();
|
||||
|
||||
return emailBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command description object for {@code /totp} including its children.
|
||||
*
|
||||
* @return the totp base command description
|
||||
*/
|
||||
private CommandDescription buildTotpBaseCommand() {
|
||||
// Register the base totp command
|
||||
CommandDescription totpBase = CommandDescription.builder()
|
||||
.parent(null)
|
||||
.labels("totp", "2fa")
|
||||
.description("TOTP commands")
|
||||
.detailedDescription("Performs actions related to two-factor authentication.")
|
||||
.executableCommand(TotpBaseCommand.class)
|
||||
.register();
|
||||
|
||||
// Register the base totp code
|
||||
CommandDescription.builder()
|
||||
.parent(totpBase)
|
||||
.labels("code", "c")
|
||||
.description("Command for logging in")
|
||||
.detailedDescription("Processes the two-factor authentication code during login.")
|
||||
.withArgument("code", "The TOTP code to use to log in", MANDATORY)
|
||||
.executableCommand(TotpCodeCommand.class)
|
||||
.register();
|
||||
|
||||
// Register totp add
|
||||
CommandDescription.builder()
|
||||
.parent(totpBase)
|
||||
.labels("add")
|
||||
.description("Enables TOTP")
|
||||
.detailedDescription("Enables two-factor authentication for your account.")
|
||||
.permission(PlayerPermission.ENABLE_TWO_FACTOR_AUTH)
|
||||
.executableCommand(AddTotpCommand.class)
|
||||
.register();
|
||||
|
||||
// Register totp confirm
|
||||
CommandDescription.builder()
|
||||
.parent(totpBase)
|
||||
.labels("confirm")
|
||||
.description("Enables TOTP after successful code")
|
||||
.detailedDescription("Saves the generated TOTP secret after confirmation.")
|
||||
.withArgument("code", "Code from the given secret from /totp add", MANDATORY)
|
||||
.permission(PlayerPermission.ENABLE_TWO_FACTOR_AUTH)
|
||||
.executableCommand(ConfirmTotpCommand.class)
|
||||
.register();
|
||||
|
||||
// Register totp remove
|
||||
CommandDescription.builder()
|
||||
.parent(totpBase)
|
||||
.labels("remove")
|
||||
.description("Removes TOTP")
|
||||
.detailedDescription("Disables two-factor authentication for your account.")
|
||||
.withArgument("code", "Current 2FA code", MANDATORY)
|
||||
.permission(PlayerPermission.DISABLE_TWO_FACTOR_AUTH)
|
||||
.executableCommand(RemoveTotpCommand.class)
|
||||
.register();
|
||||
|
||||
return totpBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the help command on all base commands, e.g. to register /authme help or /register help.
|
||||
*
|
||||
* @param commands the list of base commands to register a help child command on
|
||||
*/
|
||||
private void setHelpOnAllBases(Collection<CommandDescription> commands) {
|
||||
final List<String> helpCommandLabels = Arrays.asList("help", "hlp", "h", "sos", "?");
|
||||
|
||||
for (CommandDescription base : commands) {
|
||||
CommandDescription.builder()
|
||||
.parent(base)
|
||||
.labels(helpCommandLabels)
|
||||
.description("View help")
|
||||
.detailedDescription("View detailed help for /" + base.getLabels().get(0) + " commands.")
|
||||
.withArgument("query", "The command or query to view help for.", OPTIONAL)
|
||||
.executableCommand(HelpCommand.class)
|
||||
.register();
|
||||
}
|
||||
}
|
||||
}
|
||||
207
src/main/java/fr/xephi/authme/command/CommandMapper.java
Normal file
207
src/main/java/fr/xephi/authme/command/CommandMapper.java
Normal file
@ -0,0 +1,207 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import fr.xephi.authme.command.executable.HelpCommand;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import static fr.xephi.authme.command.FoundResultStatus.INCORRECT_ARGUMENTS;
|
||||
import static fr.xephi.authme.command.FoundResultStatus.MISSING_BASE_COMMAND;
|
||||
import static fr.xephi.authme.command.FoundResultStatus.UNKNOWN_LABEL;
|
||||
|
||||
/**
|
||||
* Maps incoming command parts to the correct {@link CommandDescription}.
|
||||
*/
|
||||
public class CommandMapper {
|
||||
|
||||
/**
|
||||
* The class of the help command, to which the base label should also be passed in the arguments.
|
||||
*/
|
||||
private static final Class<? extends ExecutableCommand> HELP_COMMAND_CLASS = HelpCommand.class;
|
||||
|
||||
private final Collection<CommandDescription> baseCommands;
|
||||
private final PermissionsManager permissionsManager;
|
||||
|
||||
@Inject
|
||||
public CommandMapper(CommandInitializer commandInitializer, PermissionsManager permissionsManager) {
|
||||
this.baseCommands = commandInitializer.getCommands();
|
||||
this.permissionsManager = permissionsManager;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Map incoming command parts to a command. This processes all parts and distinguishes the labels from arguments.
|
||||
*
|
||||
* @param sender The command sender (null if none applicable)
|
||||
* @param parts The parts to map to commands and arguments
|
||||
* @return The generated {@link FoundCommandResult}
|
||||
*/
|
||||
public FoundCommandResult mapPartsToCommand(CommandSender sender, List<String> parts) {
|
||||
if (Utils.isCollectionEmpty(parts)) {
|
||||
return new FoundCommandResult(null, parts, null, 0.0, MISSING_BASE_COMMAND);
|
||||
}
|
||||
|
||||
CommandDescription base = getBaseCommand(parts.get(0));
|
||||
if (base == null) {
|
||||
return new FoundCommandResult(null, parts, null, 0.0, MISSING_BASE_COMMAND);
|
||||
}
|
||||
|
||||
// Prefer labels: /register help goes to "Help command", not "Register command" with argument 'help'
|
||||
List<String> remainingParts = parts.subList(1, parts.size());
|
||||
CommandDescription childCommand = getSuitableChild(base, remainingParts);
|
||||
if (childCommand != null) {
|
||||
FoundResultStatus status = getPermissionAwareStatus(sender, childCommand);
|
||||
FoundCommandResult result = new FoundCommandResult(
|
||||
childCommand, parts.subList(0, 2), parts.subList(2, parts.size()), 0.0, status);
|
||||
return transformResultForHelp(result);
|
||||
} else if (hasSuitableArgumentCount(base, remainingParts.size())) {
|
||||
FoundResultStatus status = getPermissionAwareStatus(sender, base);
|
||||
return new FoundCommandResult(base, parts.subList(0, 1), parts.subList(1, parts.size()), 0.0, status);
|
||||
}
|
||||
|
||||
return getCommandWithSmallestDifference(base, parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all {@link ExecutableCommand} classes referenced in {@link CommandDescription} objects.
|
||||
*
|
||||
* @return all classes
|
||||
* @see CommandInitializer#getCommands
|
||||
*/
|
||||
public Set<Class<? extends ExecutableCommand>> getCommandClasses() {
|
||||
Set<Class<? extends ExecutableCommand>> classes = new HashSet<>(50);
|
||||
for (CommandDescription command : baseCommands) {
|
||||
classes.add(command.getExecutableCommand());
|
||||
for (CommandDescription child : command.getChildren()) {
|
||||
classes.add(child.getExecutableCommand());
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the command whose label matches the given parts the best. This method is called when
|
||||
* a successful mapping could not be performed.
|
||||
*
|
||||
* @param base the base command
|
||||
* @param parts the command parts
|
||||
* @return the closest result
|
||||
*/
|
||||
private static FoundCommandResult getCommandWithSmallestDifference(CommandDescription base, List<String> parts) {
|
||||
// Return the base command with incorrect arg count error if we only have one part
|
||||
if (parts.size() <= 1) {
|
||||
return new FoundCommandResult(base, parts, new ArrayList<>(), 0.0, INCORRECT_ARGUMENTS);
|
||||
}
|
||||
|
||||
final String childLabel = parts.get(1);
|
||||
double minDifference = Double.POSITIVE_INFINITY;
|
||||
CommandDescription closestCommand = null;
|
||||
|
||||
for (CommandDescription child : base.getChildren()) {
|
||||
double difference = getLabelDifference(child, childLabel);
|
||||
if (difference < minDifference) {
|
||||
minDifference = difference;
|
||||
closestCommand = child;
|
||||
}
|
||||
}
|
||||
|
||||
// base command may have no children, in which case we return the base command with incorrect arguments error
|
||||
if (closestCommand == null) {
|
||||
return new FoundCommandResult(
|
||||
base, parts.subList(0, 1), parts.subList(1, parts.size()), 0.0, INCORRECT_ARGUMENTS);
|
||||
}
|
||||
|
||||
FoundResultStatus status = (minDifference == 0.0) ? INCORRECT_ARGUMENTS : UNKNOWN_LABEL;
|
||||
final int partsSize = parts.size();
|
||||
List<String> labels = parts.subList(0, Math.min(closestCommand.getLabelCount(), partsSize));
|
||||
List<String> arguments = (labels.size() == partsSize)
|
||||
? new ArrayList<>()
|
||||
: parts.subList(labels.size(), partsSize);
|
||||
|
||||
return new FoundCommandResult(closestCommand, labels, arguments, minDifference, status);
|
||||
}
|
||||
|
||||
private CommandDescription getBaseCommand(String label) {
|
||||
String baseLabel = label.toLowerCase(Locale.ROOT);
|
||||
if (baseLabel.startsWith("authme:")) {
|
||||
baseLabel = baseLabel.substring("authme:".length());
|
||||
}
|
||||
for (CommandDescription command : baseCommands) {
|
||||
if (command.hasLabel(baseLabel)) {
|
||||
return command;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a child from a base command if the label and the argument count match.
|
||||
*
|
||||
* @param baseCommand The base command whose children should be checked
|
||||
* @param parts The command parts received from the invocation; the first item is the potential label and any
|
||||
* other items are command arguments. The first initial part that led to the base command should not
|
||||
* be present.
|
||||
*
|
||||
* @return A command if there was a complete match (including proper argument count), null otherwise
|
||||
*/
|
||||
private static CommandDescription getSuitableChild(CommandDescription baseCommand, List<String> parts) {
|
||||
if (Utils.isCollectionEmpty(parts)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String label = parts.get(0).toLowerCase(Locale.ROOT);
|
||||
final int argumentCount = parts.size() - 1;
|
||||
|
||||
for (CommandDescription child : baseCommand.getChildren()) {
|
||||
if (child.hasLabel(label) && hasSuitableArgumentCount(child, argumentCount)) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static FoundCommandResult transformResultForHelp(FoundCommandResult result) {
|
||||
if (result.getCommandDescription() != null
|
||||
&& HELP_COMMAND_CLASS == result.getCommandDescription().getExecutableCommand()) {
|
||||
// For "/authme help register" we have labels = [authme, help] and arguments = [register]
|
||||
// But for the help command we want labels = [authme, help] and arguments = [authme, register],
|
||||
// so we can use the arguments as the labels to the command to show help for
|
||||
List<String> arguments = new ArrayList<>(result.getArguments());
|
||||
arguments.add(0, result.getLabels().get(0));
|
||||
return new FoundCommandResult(result.getCommandDescription(), result.getLabels(),
|
||||
arguments, result.getDifference(), result.getResultStatus());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private FoundResultStatus getPermissionAwareStatus(CommandSender sender, CommandDescription command) {
|
||||
if (sender != null && !permissionsManager.hasPermission(sender, command.getPermission())) {
|
||||
return FoundResultStatus.NO_PERMISSION;
|
||||
}
|
||||
return FoundResultStatus.SUCCESS;
|
||||
}
|
||||
|
||||
private static boolean hasSuitableArgumentCount(CommandDescription command, int argumentCount) {
|
||||
int minArgs = CommandUtils.getMinNumberOfArguments(command);
|
||||
int maxArgs = CommandUtils.getMaxNumberOfArguments(command);
|
||||
|
||||
return argumentCount >= minArgs && argumentCount <= maxArgs;
|
||||
}
|
||||
|
||||
private static double getLabelDifference(CommandDescription command, String givenLabel) {
|
||||
return command.getLabels().stream()
|
||||
.map(label -> StringUtils.getDifference(label, givenLabel))
|
||||
.min(Double::compareTo)
|
||||
.orElseThrow(() -> new IllegalStateException("Command does not have any labels set"));
|
||||
}
|
||||
|
||||
}
|
||||
109
src/main/java/fr/xephi/authme/command/CommandUtils.java
Normal file
109
src/main/java/fr/xephi/authme/command/CommandUtils.java
Normal file
@ -0,0 +1,109 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility functions for {@link CommandDescription} objects.
|
||||
*/
|
||||
public final class CommandUtils {
|
||||
|
||||
private CommandUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum number of arguments required for running the command (= number of mandatory arguments).
|
||||
*
|
||||
* @param command the command to process
|
||||
* @return min number of arguments required by the command
|
||||
*/
|
||||
public static int getMinNumberOfArguments(CommandDescription command) {
|
||||
int mandatoryArguments = 0;
|
||||
for (CommandArgumentDescription argument : command.getArguments()) {
|
||||
if (!argument.isOptional()) {
|
||||
++mandatoryArguments;
|
||||
}
|
||||
}
|
||||
return mandatoryArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of arguments the command accepts.
|
||||
*
|
||||
* @param command the command to process
|
||||
* @return max number of arguments that may be passed to the command
|
||||
*/
|
||||
public static int getMaxNumberOfArguments(CommandDescription command) {
|
||||
return command.getArguments().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a hierarchical list of commands for the given command. The commands are in order:
|
||||
* the parents of the given command precede the provided command. For example, given the command
|
||||
* for {@code /authme register}, a list with {@code [{authme}, {authme register}]} is returned.
|
||||
*
|
||||
* @param command the command to build a parent list for
|
||||
* @return the parent list
|
||||
*/
|
||||
public static List<CommandDescription> constructParentList(CommandDescription command) {
|
||||
List<CommandDescription> commands = new ArrayList<>();
|
||||
CommandDescription currentCommand = command;
|
||||
while (currentCommand != null) {
|
||||
commands.add(currentCommand);
|
||||
currentCommand = currentCommand.getParent();
|
||||
}
|
||||
return Lists.reverse(commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a textual representation of the command, e.g. {@code /authme register}.
|
||||
*
|
||||
* @param command the command to create the path for
|
||||
* @return the command string
|
||||
*/
|
||||
public static String constructCommandPath(CommandDescription command) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String prefix = "/";
|
||||
for (CommandDescription ancestor : constructParentList(command)) {
|
||||
sb.append(prefix).append(ancestor.getLabels().get(0));
|
||||
prefix = " ";
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a command path with color formatting, based on the supplied labels. This includes
|
||||
* the command's arguments, as defined in the provided command description. The list of labels
|
||||
* must contain all labels to be used.
|
||||
*
|
||||
* @param command the command to read arguments from
|
||||
* @param correctLabels the labels to use (must be complete)
|
||||
* @return formatted command syntax incl. arguments
|
||||
*/
|
||||
public static String buildSyntax(CommandDescription command, List<String> correctLabels) {
|
||||
String commandSyntax = ChatColor.WHITE + "/" + correctLabels.get(0) + ChatColor.YELLOW;
|
||||
for (int i = 1; i < correctLabels.size(); ++i) {
|
||||
commandSyntax += " " + correctLabels.get(i);
|
||||
}
|
||||
for (CommandArgumentDescription argument : command.getArguments()) {
|
||||
commandSyntax += " " + formatArgument(argument);
|
||||
}
|
||||
return commandSyntax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a command argument with the proper type of brackets.
|
||||
*
|
||||
* @param argument the argument to format
|
||||
* @return the formatted argument
|
||||
*/
|
||||
public static String formatArgument(CommandArgumentDescription argument) {
|
||||
if (argument.isOptional()) {
|
||||
return "[" + argument.getName() + "]";
|
||||
}
|
||||
return "<" + argument.getName() + ">";
|
||||
}
|
||||
}
|
||||
31
src/main/java/fr/xephi/authme/command/ExecutableCommand.java
Normal file
31
src/main/java/fr/xephi/authme/command/ExecutableCommand.java
Normal file
@ -0,0 +1,31 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Base class for AuthMe commands that can be executed.
|
||||
*/
|
||||
public interface ExecutableCommand {
|
||||
|
||||
/**
|
||||
* Executes the command with the given arguments.
|
||||
*
|
||||
* @param sender the command sender (initiator of the command)
|
||||
* @param arguments the arguments
|
||||
*/
|
||||
void executeCommand(CommandSender sender, List<String> arguments);
|
||||
|
||||
/**
|
||||
* Returns the message to show to the user if the command is used with the wrong arguments.
|
||||
* If null is returned, the standard help (/<i>command</i> help) output is shown.
|
||||
*
|
||||
* @return the message explaining the command's usage, or {@code null} for default behavior
|
||||
*/
|
||||
default MessageKey getArgumentsMismatchMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Result of a command mapping by {@link CommandHandler}. An object of this class represents a successful mapping
|
||||
* as well as erroneous ones, as communicated with {@link FoundResultStatus}.
|
||||
* <p>
|
||||
* Fields other than {@link FoundResultStatus} are available depending, among other factors, on the status:
|
||||
* <ul>
|
||||
* <li>{@link FoundResultStatus#SUCCESS} entails that mapping the input to a command was successful. Therefore,
|
||||
* the command description, labels and arguments are set. The difference is 0.0.</li>
|
||||
* <li>{@link FoundResultStatus#INCORRECT_ARGUMENTS}: The received parts could be mapped to a command but the argument
|
||||
* count doesn't match. Guarantees that the command description field is not null; difference is 0.0</li>
|
||||
* <li>{@link FoundResultStatus#UNKNOWN_LABEL}: The labels could not be mapped to a command. The command description
|
||||
* may be set to the most similar command, or it may be null. Difference is above 0.0.</li>
|
||||
* <li>{@link FoundResultStatus#NO_PERMISSION}: The command could be matched properly but the sender does not have
|
||||
* permission to execute it.</li>
|
||||
* <li>{@link FoundResultStatus#MISSING_BASE_COMMAND} should never occur. All other fields may be null and any further
|
||||
* processing of the object should be aborted.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class FoundCommandResult {
|
||||
|
||||
/**
|
||||
* The command description instance.
|
||||
*/
|
||||
private final CommandDescription commandDescription;
|
||||
/**
|
||||
* The labels used to invoke the command. This may be different for the same {@link ExecutableCommand} instance
|
||||
* if multiple labels have been defined, e.g. "/authme register" and "/authme reg".
|
||||
*/
|
||||
private final List<String> labels;
|
||||
/** The command arguments. */
|
||||
private final List<String> arguments;
|
||||
/** The difference between the matched command and the supplied labels. */
|
||||
private final double difference;
|
||||
/** The status of the result (see class description). */
|
||||
private final FoundResultStatus resultStatus;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param commandDescription The command description.
|
||||
* @param labels The labels used to access the command.
|
||||
* @param arguments The command arguments.
|
||||
* @param difference The difference between the supplied labels and the matched command.
|
||||
* @param resultStatus The status of the result.
|
||||
*/
|
||||
public FoundCommandResult(CommandDescription commandDescription, List<String> labels, List<String> arguments,
|
||||
double difference, FoundResultStatus resultStatus) {
|
||||
this.commandDescription = commandDescription;
|
||||
this.labels = labels;
|
||||
this.arguments = arguments;
|
||||
this.difference = difference;
|
||||
this.resultStatus = resultStatus;
|
||||
}
|
||||
|
||||
public CommandDescription getCommandDescription() {
|
||||
return this.commandDescription;
|
||||
}
|
||||
|
||||
public List<String> getArguments() {
|
||||
return this.arguments;
|
||||
}
|
||||
|
||||
public List<String> getLabels() {
|
||||
return this.labels;
|
||||
}
|
||||
|
||||
public double getDifference() {
|
||||
return difference;
|
||||
}
|
||||
|
||||
public FoundResultStatus getResultStatus() {
|
||||
return resultStatus;
|
||||
}
|
||||
|
||||
}
|
||||
18
src/main/java/fr/xephi/authme/command/FoundResultStatus.java
Normal file
18
src/main/java/fr/xephi/authme/command/FoundResultStatus.java
Normal file
@ -0,0 +1,18 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
/**
|
||||
* Result status for mapping command parts. See {@link FoundCommandResult} for a detailed description of the states.
|
||||
*/
|
||||
public enum FoundResultStatus {
|
||||
|
||||
SUCCESS,
|
||||
|
||||
INCORRECT_ARGUMENTS,
|
||||
|
||||
UNKNOWN_LABEL,
|
||||
|
||||
NO_PERMISSION,
|
||||
|
||||
MISSING_BASE_COMMAND
|
||||
|
||||
}
|
||||
45
src/main/java/fr/xephi/authme/command/PlayerCommand.java
Normal file
45
src/main/java/fr/xephi/authme/command/PlayerCommand.java
Normal file
@ -0,0 +1,45 @@
|
||||
package fr.xephi.authme.command;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Common base type for player-only commands, handling the verification that the command sender is indeed a player.
|
||||
*/
|
||||
public abstract class PlayerCommand implements ExecutableCommand {
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
if (sender instanceof Player) {
|
||||
runCommand((Player) sender, arguments);
|
||||
} else {
|
||||
String alternative = getAlternativeCommand();
|
||||
if (alternative != null) {
|
||||
sender.sendMessage("此命令仅限玩家使用!请使用 " + alternative + " .");
|
||||
} else {
|
||||
sender.sendMessage("此命令只能被玩家执行.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the command with the given player and arguments.
|
||||
*
|
||||
* @param player the player who initiated the command
|
||||
* @param arguments the arguments supplied with the command
|
||||
*/
|
||||
protected abstract void runCommand(Player player, List<String> arguments);
|
||||
|
||||
/**
|
||||
* Returns an alternative command (textual representation) that is not restricted to players only.
|
||||
* Example: {@code "/authme register <playerName> <password>"}
|
||||
*
|
||||
* @return Alternative command not restricted to players, or null if not applicable
|
||||
*/
|
||||
protected String getAlternativeCommand() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package fr.xephi.authme.command.executable;
|
||||
|
||||
import fr.xephi.authme.command.CommandMapper;
|
||||
import fr.xephi.authme.command.CommandUtils;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.command.FoundCommandResult;
|
||||
import fr.xephi.authme.command.FoundResultStatus;
|
||||
import fr.xephi.authme.command.help.HelpProvider;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
import static fr.xephi.authme.command.FoundResultStatus.MISSING_BASE_COMMAND;
|
||||
import static fr.xephi.authme.command.FoundResultStatus.UNKNOWN_LABEL;
|
||||
import static fr.xephi.authme.command.help.HelpProvider.ALL_OPTIONS;
|
||||
import static fr.xephi.authme.command.help.HelpProvider.SHOW_ALTERNATIVES;
|
||||
import static fr.xephi.authme.command.help.HelpProvider.SHOW_CHILDREN;
|
||||
import static fr.xephi.authme.command.help.HelpProvider.SHOW_COMMAND;
|
||||
import static fr.xephi.authme.command.help.HelpProvider.SHOW_DESCRIPTION;
|
||||
|
||||
/**
|
||||
* Displays help information to a user.
|
||||
*/
|
||||
public class HelpCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private CommandMapper commandMapper;
|
||||
|
||||
@Inject
|
||||
private HelpProvider helpProvider;
|
||||
|
||||
|
||||
// Convention: arguments is not the actual invoked arguments but the command that was invoked,
|
||||
// e.g. "/authme help register" would typically be arguments = [register], but here we pass [authme, register]
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
FoundCommandResult result = commandMapper.mapPartsToCommand(sender, arguments);
|
||||
|
||||
FoundResultStatus resultStatus = result.getResultStatus();
|
||||
if (MISSING_BASE_COMMAND.equals(resultStatus)) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Could not get base command");
|
||||
return;
|
||||
} else if (UNKNOWN_LABEL.equals(resultStatus)) {
|
||||
if (result.getCommandDescription() == null) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Unknown command");
|
||||
return;
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.GOLD + "Assuming " + ChatColor.WHITE
|
||||
+ CommandUtils.constructCommandPath(result.getCommandDescription()));
|
||||
}
|
||||
}
|
||||
|
||||
int mappedCommandLevel = result.getCommandDescription().getLabelCount();
|
||||
if (mappedCommandLevel == 1) {
|
||||
helpProvider.outputHelp(sender, result,
|
||||
SHOW_COMMAND | SHOW_DESCRIPTION | SHOW_CHILDREN | SHOW_ALTERNATIVES);
|
||||
} else {
|
||||
helpProvider.outputHelp(sender, result, ALL_OPTIONS);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Shows all accounts registered by the same IP address for the given player name or IP address.
|
||||
*/
|
||||
public class AccountsCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(final CommandSender sender, List<String> arguments) {
|
||||
// TODO #1366: last IP vs. registration IP?
|
||||
final String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
|
||||
|
||||
// Assumption: a player name cannot contain '.'
|
||||
if (playerName.contains(".")) {
|
||||
bukkitService.runTaskAsynchronously(() -> {
|
||||
List<String> accountList = dataSource.getAllAuthsByIp(playerName);
|
||||
if (accountList.isEmpty()) {
|
||||
sender.sendMessage("[AuthMe] This IP does not exist in the database.");
|
||||
} else if (accountList.size() == 1) {
|
||||
sender.sendMessage("[AuthMe] " + playerName + " is a single account player");
|
||||
} else {
|
||||
outputAccountsList(sender, playerName, accountList);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
bukkitService.runTaskAsynchronously(() -> {
|
||||
PlayerAuth auth = dataSource.getAuth(playerName.toLowerCase(Locale.ROOT));
|
||||
if (auth == null) {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
return;
|
||||
} else if (auth.getLastIp() == null) {
|
||||
sender.sendMessage("No known last IP address for player");
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> accountList = dataSource.getAllAuthsByIp(auth.getLastIp());
|
||||
if (accountList.isEmpty()) {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
} else if (accountList.size() == 1) {
|
||||
sender.sendMessage("[AuthMe] " + playerName + " is a single account player");
|
||||
} else {
|
||||
outputAccountsList(sender, playerName, accountList);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void outputAccountsList(CommandSender sender, String playerName, List<String> accountList) {
|
||||
sender.sendMessage("[AuthMe] " + playerName + " has " + accountList.size() + " accounts.");
|
||||
String message = "[AuthMe] " + String.join(", ", accountList) + ".";
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AuthMe base command; shows the version and some command pointers.
|
||||
*/
|
||||
public class AuthMeCommand implements ExecutableCommand {
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.GREEN + "This server is running " + AuthMe.getPluginName() + " v"
|
||||
+ AuthMe.getPluginVersion() + " b" + AuthMe.getPluginBuildNumber()+ "! " + ChatColor.RED + "<3");
|
||||
sender.sendMessage(ChatColor.YELLOW + "Use the command " + ChatColor.GOLD + "/authme help" + ChatColor.YELLOW
|
||||
+ " to view help.");
|
||||
sender.sendMessage(ChatColor.YELLOW + "Use the command " + ChatColor.GOLD + "/authme about" + ChatColor.YELLOW
|
||||
+ " to view about.");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.service.BackupService;
|
||||
import fr.xephi.authme.service.BackupService.BackupCause;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command to perform a backup.
|
||||
*/
|
||||
public class BackupCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private BackupService backupService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
backupService.doBackup(BackupCause.COMMAND, sender);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.service.ValidationService.ValidationResult;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Admin command for changing a player's password.
|
||||
*/
|
||||
public class ChangePasswordAdminCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
// Get the player and password
|
||||
final String playerName = arguments.get(0);
|
||||
final String playerPass = arguments.get(1);
|
||||
|
||||
// Validate the password
|
||||
ValidationResult validationResult = validationService.validatePassword(playerPass, playerName);
|
||||
if (validationResult.hasError()) {
|
||||
commonService.send(sender, validationResult.getMessageKey(), validationResult.getArgs());
|
||||
} else {
|
||||
management.performPasswordChangeAsAdmin(sender, playerName, playerPass);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.datasource.converter.Converter;
|
||||
import fr.xephi.authme.datasource.converter.CrazyLoginConverter;
|
||||
import fr.xephi.authme.datasource.converter.LoginSecurityConverter;
|
||||
import fr.xephi.authme.datasource.converter.MySqlToSqlite;
|
||||
import fr.xephi.authme.datasource.converter.RakamakConverter;
|
||||
import fr.xephi.authme.datasource.converter.RoyalAuthConverter;
|
||||
import fr.xephi.authme.datasource.converter.SqliteToSql;
|
||||
import fr.xephi.authme.datasource.converter.VAuthConverter;
|
||||
import fr.xephi.authme.datasource.converter.XAuthConverter;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Converter command: launches conversion based on its parameters.
|
||||
*/
|
||||
public class ConverterCommand implements ExecutableCommand {
|
||||
|
||||
@VisibleForTesting
|
||||
static final Map<String, Class<? extends Converter>> CONVERTERS = getConverters();
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(ConverterCommand.class);
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private Factory<Converter> converterFactory;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
Class<? extends Converter> converterClass = getConverterClassFromArgs(arguments);
|
||||
if (converterClass == null) {
|
||||
sender.sendMessage("Converters: " + String.join(", ", CONVERTERS.keySet()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the proper converter instance
|
||||
final Converter converter = converterFactory.newInstance(converterClass);
|
||||
|
||||
// Run the convert job
|
||||
bukkitService.runTaskAsynchronously(() -> {
|
||||
try {
|
||||
converter.execute(sender);
|
||||
} catch (Exception e) {
|
||||
commonService.send(sender, MessageKey.ERROR);
|
||||
logger.logException("Error during conversion:", e);
|
||||
}
|
||||
});
|
||||
|
||||
// Show a status message
|
||||
sender.sendMessage("[AuthMe] Successfully started " + arguments.get(0));
|
||||
}
|
||||
|
||||
private static Class<? extends Converter> getConverterClassFromArgs(List<String> arguments) {
|
||||
return arguments.isEmpty()
|
||||
? null
|
||||
: CONVERTERS.get(arguments.get(0).toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a map with all available converters.
|
||||
*
|
||||
* @return map with all available converters
|
||||
*/
|
||||
private static Map<String, Class<? extends Converter>> getConverters() {
|
||||
return ImmutableSortedMap.<String, Class<? extends Converter>>naturalOrder()
|
||||
.put("xauth", XAuthConverter.class)
|
||||
.put("crazylogin", CrazyLoginConverter.class)
|
||||
.put("rakamak", RakamakConverter.class)
|
||||
.put("royalauth", RoyalAuthConverter.class)
|
||||
.put("vauth", VAuthConverter.class)
|
||||
.put("sqlitetosql", SqliteToSql.class)
|
||||
.put("mysqltosqlite", MySqlToSqlite.class)
|
||||
.put("loginsecurity", LoginSecurityConverter.class)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.SpawnLoader;
|
||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||
import fr.xephi.authme.util.TeleportUtils;
|
||||
import org.bukkit.entity.Player;
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Teleports the player to the first spawn.
|
||||
*/
|
||||
public class FirstSpawnCommand extends PlayerCommand {
|
||||
@Inject
|
||||
private Settings settings;
|
||||
@Inject
|
||||
private SpawnLoader spawnLoader;
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
if (spawnLoader.getFirstSpawn() == null) {
|
||||
player.sendMessage("[AuthMe] First spawn has failed, please try to define the first spawn");
|
||||
} else {
|
||||
//String name= player.getName();
|
||||
if(settings.getProperty(SecuritySettings.SMART_ASYNC_TELEPORT)) {
|
||||
TeleportUtils.teleport(player, spawnLoader.getFirstSpawn());
|
||||
} else {
|
||||
player.teleport(spawnLoader.getFirstSpawn());
|
||||
}
|
||||
//player.teleport(spawnLoader.getFirstSpawn());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
import static fr.xephi.authme.permission.PlayerPermission.CAN_LOGIN_BE_FORCED;
|
||||
|
||||
/**
|
||||
* Forces the login of a player, i.e. logs the player in without the need of a (correct) password.
|
||||
*/
|
||||
public class ForceLoginCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
// Get the player query
|
||||
String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
|
||||
|
||||
Player player = bukkitService.getPlayerExact(playerName);
|
||||
if (player == null || !player.isOnline()) {
|
||||
sender.sendMessage("Player needs to be online!");
|
||||
} else if (!permissionsManager.hasPermission(player, CAN_LOGIN_BE_FORCED)) {
|
||||
sender.sendMessage("You cannot force login the player " + playerName + "!");
|
||||
} else {
|
||||
management.forceLogin(player);
|
||||
sender.sendMessage("Force login for " + playerName + " performed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Returns a player's email.
|
||||
*/
|
||||
public class GetEmailCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
|
||||
|
||||
DataSourceValue<String> email = dataSource.getEmail(playerName);
|
||||
if (email.rowExists()) {
|
||||
sender.sendMessage("[AuthMe] " + playerName + "'s email: " + email.getValue());
|
||||
} else {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.util.PlayerUtils;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
public class GetIpCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
String playerName = arguments.get(0);
|
||||
Player player = bukkitService.getPlayerExact(playerName);
|
||||
PlayerAuth auth = dataSource.getAuth(playerName);
|
||||
|
||||
if (player != null) {
|
||||
sender.sendMessage("Current IP of " + player.getName() + " is " + PlayerUtils.getPlayerIp(player)
|
||||
+ ":" + player.getAddress().getPort());
|
||||
}
|
||||
|
||||
if (auth == null) {
|
||||
String displayName = player == null ? playerName : player.getName();
|
||||
sender.sendMessage(displayName + " is not registered in the database");
|
||||
} else {
|
||||
sender.sendMessage("Database: last IP: " + auth.getLastIp() + ", registration IP: "
|
||||
+ auth.getRegistrationIp());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Returns the last login date of the given user.
|
||||
*/
|
||||
public class LastLoginCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
// Get the player
|
||||
String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
|
||||
|
||||
PlayerAuth auth = dataSource.getAuth(playerName);
|
||||
if (auth == null) {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the last login date
|
||||
final Long lastLogin = auth.getLastLogin();
|
||||
final String lastLoginDate = lastLogin == null ? "never" : new Date(lastLogin).toString();
|
||||
|
||||
// Show the player status
|
||||
sender.sendMessage("[AuthMe] " + playerName + " last login: " + lastLoginDate);
|
||||
if (lastLogin != null) {
|
||||
sender.sendMessage("[AuthMe] The player " + playerName + " last logged in "
|
||||
+ createLastLoginIntervalMessage(lastLogin) + " ago");
|
||||
}
|
||||
sender.sendMessage("[AuthMe] Last player's IP: " + auth.getLastIp());
|
||||
}
|
||||
|
||||
private static String createLastLoginIntervalMessage(long lastLogin) {
|
||||
final long diff = System.currentTimeMillis() - lastLogin;
|
||||
return (int) (diff / 86400000) + " days "
|
||||
+ (int) (diff / 3600000 % 24) + " hours "
|
||||
+ (int) (diff / 60000 % 60) + " mins "
|
||||
+ (int) (diff / 1000 % 60) + " secs";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.task.purge.PurgeService;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Command for purging data of banned players. Depending on the settings
|
||||
* it purges (deletes) data from third-party plugins as well.
|
||||
*/
|
||||
public class PurgeBannedPlayersCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private PurgeService purgeService;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
// Get the list of banned players
|
||||
Set<OfflinePlayer> bannedPlayers = bukkitService.getBannedPlayers();
|
||||
Set<String> namedBanned = new HashSet<>(bannedPlayers.size());
|
||||
for (OfflinePlayer offlinePlayer : bannedPlayers) {
|
||||
namedBanned.add(offlinePlayer.getName().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
purgeService.purgePlayers(sender, namedBanned, bannedPlayers.toArray(new OfflinePlayer[bannedPlayers.size()]));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.task.purge.PurgeService;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for purging the data of players which have not been online for a given number
|
||||
* of days. Depending on the settings, this removes player data in third-party plugins as well.
|
||||
*/
|
||||
public class PurgeCommand implements ExecutableCommand {
|
||||
|
||||
private static final int MINIMUM_LAST_SEEN_DAYS = 30;
|
||||
|
||||
@Inject
|
||||
private PurgeService purgeService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
// Get the days parameter
|
||||
String daysStr = arguments.get(0);
|
||||
|
||||
// Convert the days string to an integer value, and make sure it's valid
|
||||
Integer days = Ints.tryParse(daysStr);
|
||||
if (days == null) {
|
||||
sender.sendMessage(ChatColor.RED + "The value you've entered is invalid!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate the value
|
||||
if (days < MINIMUM_LAST_SEEN_DAYS) {
|
||||
sender.sendMessage(ChatColor.RED + "You can only purge data older than "
|
||||
+ MINIMUM_LAST_SEEN_DAYS + " days");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a calender instance to determine the date
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.add(Calendar.DATE, -days);
|
||||
long until = calendar.getTimeInMillis();
|
||||
|
||||
// Run the purge
|
||||
purgeService.runPurge(sender, until);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Removes the stored last position of a user or of all.
|
||||
*/
|
||||
public class PurgeLastPositionCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
|
||||
|
||||
if ("*".equals(playerName)) {
|
||||
for (PlayerAuth auth : dataSource.getAllAuths()) {
|
||||
resetLastPosition(auth);
|
||||
dataSource.updateQuitLoc(auth);
|
||||
// TODO: send an update when a messaging service will be implemented (QUITLOC)
|
||||
}
|
||||
sender.sendMessage("All players last position locations are now reset");
|
||||
} else {
|
||||
// Get the user auth and make sure the user exists
|
||||
PlayerAuth auth = dataSource.getAuth(playerName);
|
||||
if (auth == null) {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
return;
|
||||
}
|
||||
|
||||
resetLastPosition(auth);
|
||||
dataSource.updateQuitLoc(auth);
|
||||
// TODO: send an update when a messaging service will be implemented (QUITLOC)
|
||||
sender.sendMessage(playerName + "'s last position location is now reset");
|
||||
}
|
||||
}
|
||||
|
||||
private static void resetLastPosition(PlayerAuth auth) {
|
||||
auth.setQuitLocX(0d);
|
||||
auth.setQuitLocY(0d);
|
||||
auth.setQuitLocZ(0d);
|
||||
auth.setWorld("world");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.task.purge.PurgeExecutor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
/**
|
||||
* Command to purge a player.
|
||||
*/
|
||||
public class PurgePlayerCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private PurgeExecutor purgeExecutor;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
String option = arguments.size() > 1 ? arguments.get(1) : null;
|
||||
bukkitService.runTaskAsynchronously(
|
||||
() -> executeCommand(sender, arguments.get(0), option));
|
||||
}
|
||||
|
||||
private void executeCommand(CommandSender sender, String name, String option) {
|
||||
if ("force".equals(option) || !dataSource.isAuthAvailable(name)) {
|
||||
OfflinePlayer offlinePlayer = bukkitService.getOfflinePlayer(name);
|
||||
purgeExecutor.executePurge(singletonList(offlinePlayer), singletonList(name.toLowerCase(Locale.ROOT)));
|
||||
sender.sendMessage("Purged data for player " + name);
|
||||
} else {
|
||||
sender.sendMessage("This player is still registered! Are you sure you want to proceed? "
|
||||
+ "Use '/authme purgeplayer " + name + " force' to run the command anyway");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
import static java.time.Instant.ofEpochMilli;
|
||||
|
||||
/**
|
||||
* Command showing the most recent logged in players.
|
||||
*/
|
||||
public class RecentPlayersCommand implements ExecutableCommand {
|
||||
|
||||
/** DateTime formatter, producing Strings such as "10:42 AM, 11 Jul". */
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("hh:mm a, dd MMM");
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
List<PlayerAuth> recentPlayers = dataSource.getRecentlyLoggedInPlayers();
|
||||
|
||||
sender.sendMessage(ChatColor.BLUE + "[AuthMe] Recently logged in players");
|
||||
for (PlayerAuth auth : recentPlayers) {
|
||||
sender.sendMessage(formatPlayerMessage(auth));
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ZoneId getZoneId() {
|
||||
return ZoneId.systemDefault();
|
||||
}
|
||||
|
||||
private String formatPlayerMessage(PlayerAuth auth) {
|
||||
String lastLoginText;
|
||||
if (auth.getLastLogin() == null) {
|
||||
lastLoginText = "never";
|
||||
} else {
|
||||
LocalDateTime lastLogin = LocalDateTime.ofInstant(ofEpochMilli(auth.getLastLogin()), getZoneId());
|
||||
lastLoginText = DATE_FORMAT.format(lastLogin);
|
||||
}
|
||||
|
||||
return "- " + auth.getRealName() + " (" + lastLoginText + " with IP " + auth.getLastIp() + ")";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.security.PasswordSecurity;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.service.ValidationService.ValidationResult;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Admin command to register a user.
|
||||
*/
|
||||
public class RegisterAdminCommand implements ExecutableCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(RegisterAdminCommand.class);
|
||||
|
||||
@Inject
|
||||
private PasswordSecurity passwordSecurity;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(final CommandSender sender, List<String> arguments) {
|
||||
// Get the player name and password
|
||||
final String playerName = arguments.get(0);
|
||||
final String playerPass = arguments.get(1);
|
||||
final String playerNameLowerCase = playerName.toLowerCase(Locale.ROOT);
|
||||
|
||||
// Command logic
|
||||
ValidationResult passwordValidation = validationService.validatePassword(playerPass, playerName);
|
||||
if (passwordValidation.hasError()) {
|
||||
commonService.send(sender, passwordValidation.getMessageKey(), passwordValidation.getArgs());
|
||||
return;
|
||||
}
|
||||
|
||||
bukkitService.runTaskOptionallyAsync(() -> {
|
||||
if (dataSource.isAuthAvailable(playerNameLowerCase)) {
|
||||
commonService.send(sender, MessageKey.NAME_ALREADY_REGISTERED);
|
||||
return;
|
||||
}
|
||||
HashedPassword hashedPassword = passwordSecurity.computeHash(playerPass, playerNameLowerCase);
|
||||
PlayerAuth auth = PlayerAuth.builder()
|
||||
.name(playerNameLowerCase)
|
||||
.realName(playerName)
|
||||
.password(hashedPassword)
|
||||
.registrationDate(System.currentTimeMillis())
|
||||
.build();
|
||||
|
||||
if (!dataSource.saveAuth(auth)) {
|
||||
commonService.send(sender, MessageKey.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
commonService.send(sender, MessageKey.REGISTER_SUCCESS);
|
||||
logger.info(sender.getName() + " registered " + playerName);
|
||||
final Player player = bukkitService.getPlayerExact(playerName);
|
||||
if (player != null) {
|
||||
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() ->
|
||||
player.kickPlayer(commonService.retrieveSingleMessage(player, MessageKey.KICK_FOR_ADMIN_REGISTER)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import ch.jalu.injector.factory.SingletonStore;
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.initialization.Reloadable;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.SettingsWarner;
|
||||
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The reload command.
|
||||
*/
|
||||
public class ReloadCommand implements ExecutableCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(ReloadCommand.class);
|
||||
|
||||
@Inject
|
||||
private AuthMe plugin;
|
||||
|
||||
@Inject
|
||||
private Settings settings;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private SettingsWarner settingsWarner;
|
||||
|
||||
@Inject
|
||||
private SingletonStore<Reloadable> reloadableStore;
|
||||
|
||||
@Inject
|
||||
private SingletonStore<SettingsDependent> settingsDependentStore;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
try {
|
||||
settings.reload();
|
||||
ConsoleLoggerFactory.reloadSettings(settings);
|
||||
settingsWarner.logWarningsForMisconfigurations();
|
||||
|
||||
// We do not change database type for consistency issues, but we'll output a note in the logs
|
||||
if (!settings.getProperty(DatabaseSettings.BACKEND).equals(dataSource.getType())) {
|
||||
Utils.logAndSendMessage(sender, "Note: cannot change database type during /authme reload");
|
||||
}
|
||||
performReloadOnServices();
|
||||
commonService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS);
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage("Error occurred during reload of AuthMe: aborting");
|
||||
logger.logException("Aborting! Encountered exception during reload of AuthMe:", e);
|
||||
plugin.stopOrUnload();
|
||||
}
|
||||
}
|
||||
|
||||
private void performReloadOnServices() {
|
||||
reloadableStore.retrieveAllOfType()
|
||||
.forEach(r -> r.reload());
|
||||
|
||||
settingsDependentStore.retrieveAllOfType()
|
||||
.forEach(s -> s.reload(settings));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Admin command for setting an email to an account.
|
||||
*/
|
||||
public class SetEmailCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(final CommandSender sender, List<String> arguments) {
|
||||
// Get the player name and email address
|
||||
final String playerName = arguments.get(0);
|
||||
final String playerEmail = arguments.get(1);
|
||||
|
||||
// Validate the email address
|
||||
if (!validationService.validateEmail(playerEmail)) {
|
||||
commonService.send(sender, MessageKey.INVALID_EMAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
bukkitService.runTaskOptionallyAsync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Validate the user
|
||||
PlayerAuth auth = dataSource.getAuth(playerName);
|
||||
if (auth == null) {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
return;
|
||||
} else if (!validationService.isEmailFreeForRegistration(playerEmail, sender)) {
|
||||
commonService.send(sender, MessageKey.EMAIL_ALREADY_USED_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the email address
|
||||
auth.setEmail(playerEmail);
|
||||
if (!dataSource.updateEmail(auth)) {
|
||||
commonService.send(sender, MessageKey.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the player cache
|
||||
if (playerCache.getAuth(playerName) != null) {
|
||||
playerCache.updatePlayer(auth);
|
||||
}
|
||||
|
||||
// Show a status message
|
||||
commonService.send(sender, MessageKey.EMAIL_CHANGED_SUCCESS);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.settings.SpawnLoader;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
public class SetFirstSpawnCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private SpawnLoader spawnLoader;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
if (spawnLoader.setFirstSpawn(player.getLocation())) {
|
||||
player.sendMessage("[AuthMe] Correctly defined new first spawn point");
|
||||
} else {
|
||||
player.sendMessage("[AuthMe] SetFirstSpawn has failed, please retry");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.settings.SpawnLoader;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
public class SetSpawnCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private SpawnLoader spawnLoader;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
if (spawnLoader.setSpawn(player.getLocation())) {
|
||||
player.sendMessage("[AuthMe] Correctly defined new spawn point");
|
||||
} else {
|
||||
player.sendMessage("[AuthMe] SetSpawn has failed, please retry");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.settings.SpawnLoader;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
public class SpawnCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private SpawnLoader spawnLoader;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
if (spawnLoader.getSpawn() == null) {
|
||||
player.sendMessage("[AuthMe] Spawn has failed, please try to define the spawn");
|
||||
} else {
|
||||
player.teleport(spawnLoader.getSpawn());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.CommandMapper;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.command.FoundCommandResult;
|
||||
import fr.xephi.authme.command.help.HelpProvider;
|
||||
import fr.xephi.authme.service.AntiBotService;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Display or change the status of the antibot mod.
|
||||
*/
|
||||
public class SwitchAntiBotCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private AntiBotService antiBotService;
|
||||
|
||||
@Inject
|
||||
private CommandMapper commandMapper;
|
||||
|
||||
@Inject
|
||||
private HelpProvider helpProvider;
|
||||
|
||||
@Override
|
||||
public void executeCommand(final CommandSender sender, List<String> arguments) {
|
||||
if (arguments.isEmpty()) {
|
||||
sender.sendMessage("[AuthMe] AntiBot status: " + antiBotService.getAntiBotStatus().name());
|
||||
return;
|
||||
}
|
||||
|
||||
String newState = arguments.get(0);
|
||||
|
||||
// Enable or disable the mod
|
||||
if ("ON".equalsIgnoreCase(newState)) {
|
||||
antiBotService.overrideAntiBotStatus(true);
|
||||
sender.sendMessage("[AuthMe] AntiBot Manual Override: enabled!");
|
||||
} else if ("OFF".equalsIgnoreCase(newState)) {
|
||||
antiBotService.overrideAntiBotStatus(false);
|
||||
sender.sendMessage("[AuthMe] AntiBot Manual Override: disabled!");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Invalid AntiBot mode!");
|
||||
FoundCommandResult result = commandMapper.mapPartsToCommand(sender, Arrays.asList("authme", "antibot"));
|
||||
helpProvider.outputHelp(sender, result, HelpProvider.SHOW_ARGUMENTS);
|
||||
sender.sendMessage(ChatColor.GOLD + "Detailed help: " + ChatColor.WHITE + "/authme help antibot");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command to disable two-factor authentication for a user.
|
||||
*/
|
||||
public class TotpDisableAdminCommand implements ExecutableCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(TotpDisableAdminCommand.class);
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
String player = arguments.get(0);
|
||||
|
||||
PlayerAuth auth = dataSource.getAuth(player);
|
||||
if (auth == null) {
|
||||
messages.send(sender, MessageKey.UNKNOWN_USER);
|
||||
} else if (auth.getTotpKey() == null) {
|
||||
sender.sendMessage(ChatColor.RED + "Player '" + player + "' does not have two-factor auth enabled");
|
||||
} else {
|
||||
removeTotpKey(sender, player);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeTotpKey(CommandSender sender, String player) {
|
||||
if (dataSource.removeTotpKey(player)) {
|
||||
sender.sendMessage("Disabled two-factor authentication successfully for '" + player + "'");
|
||||
logger.info(sender.getName() + " disable two-factor authentication for '" + player + "'");
|
||||
|
||||
Player onlinePlayer = bukkitService.getPlayerExact(player);
|
||||
if (onlinePlayer != null) {
|
||||
messages.send(onlinePlayer, MessageKey.TWO_FACTOR_REMOVED_SUCCESS);
|
||||
}
|
||||
} else {
|
||||
messages.send(sender, MessageKey.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command to see whether a user has enabled two-factor authentication.
|
||||
*/
|
||||
public class TotpViewStatusCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
String player = arguments.get(0);
|
||||
|
||||
PlayerAuth auth = dataSource.getAuth(player);
|
||||
if (auth == null) {
|
||||
messages.send(sender, MessageKey.UNKNOWN_USER);
|
||||
} else if (auth.getTotpKey() == null) {
|
||||
sender.sendMessage(ChatColor.RED + "Player '" + player + "' does NOT have two-factor auth enabled");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "Player '" + player + "' has enabled two-factor authentication");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Admin command to unregister a player.
|
||||
*/
|
||||
public class UnregisterAdminCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
UnregisterAdminCommand() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeCommand(final CommandSender sender, List<String> arguments) {
|
||||
String playerName = arguments.get(0);
|
||||
|
||||
// Make sure the user exists
|
||||
if (!dataSource.isAuthAvailable(playerName)) {
|
||||
commonService.send(sender, MessageKey.UNKNOWN_USER);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the player from the server and perform unregister
|
||||
Player target = bukkitService.getPlayerExact(playerName);
|
||||
management.performUnregisterByAdmin(sender, playerName, target);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.command.help.HelpMessagesService;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.service.HelpTranslationGenerator;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Messages command, updates the user's help messages file with any missing files
|
||||
* from the provided file in the JAR.
|
||||
*/
|
||||
public class UpdateHelpMessagesCommand implements ExecutableCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(UpdateHelpMessagesCommand.class);
|
||||
|
||||
@Inject
|
||||
private HelpTranslationGenerator helpTranslationGenerator;
|
||||
@Inject
|
||||
private HelpMessagesService helpMessagesService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
try {
|
||||
File updatedFile = helpTranslationGenerator.updateHelpFile();
|
||||
sender.sendMessage("Successfully updated the help file '" + updatedFile.getName() + "'");
|
||||
helpMessagesService.reloadMessagesFile();
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage("Could not update help file: " + e.getMessage());
|
||||
logger.logException("Could not update help file:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import org.bukkit.ChatColor;
|
||||
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 {
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
// Show some version info
|
||||
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 + "Authors:");
|
||||
Collection<Player> onlinePlayers = bukkitService.getOnlinePlayers();
|
||||
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
|
||||
+ "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 " + new SimpleDateFormat("yyyy").format(new Date())
|
||||
+ ". Released under GPL v3 License.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a developer with proper styling.
|
||||
*
|
||||
* @param sender The command sender
|
||||
* @param name The display name of the developer
|
||||
* @param minecraftName The Minecraft username of the developer, if available
|
||||
* @param function The function of the developer
|
||||
* @param onlinePlayers The list of online players
|
||||
*/
|
||||
private static void printDeveloper(CommandSender sender, String name, String minecraftName, String function,
|
||||
Collection<Player> onlinePlayers) {
|
||||
// Print the name
|
||||
StringBuilder msg = new StringBuilder();
|
||||
msg.append(" ")
|
||||
.append(ChatColor.WHITE)
|
||||
.append(name);
|
||||
|
||||
// Append the Minecraft name
|
||||
msg.append(ChatColor.GRAY).append(" // ").append(ChatColor.WHITE).append(minecraftName);
|
||||
msg.append(ChatColor.GRAY).append(ChatColor.ITALIC).append(" (").append(function).append(")");
|
||||
|
||||
// Show the online status
|
||||
if (isPlayerOnline(minecraftName, onlinePlayers)) {
|
||||
msg.append(ChatColor.GREEN).append(ChatColor.ITALIC).append(" (In-Game)");
|
||||
}
|
||||
|
||||
// Print the message
|
||||
sender.sendMessage(msg.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a player is online.
|
||||
*
|
||||
* @param minecraftName The Minecraft player name
|
||||
* @param onlinePlayers List of online players
|
||||
*
|
||||
* @return True if the player is online, false otherwise
|
||||
*/
|
||||
private static boolean isPlayerOnline(String minecraftName, Collection<Player> onlinePlayers) {
|
||||
for (Player player : onlinePlayers) {
|
||||
if (player.getName().equalsIgnoreCase(minecraftName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.service.GeoIpService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.settings.properties.ProtectionSettings;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Shows the GeoIP information as returned by the geoIpService.
|
||||
*/
|
||||
class CountryLookup implements DebugSection {
|
||||
|
||||
private static final Pattern IS_IP_ADDR = Pattern.compile("(\\d{1,3}\\.){3}\\d{1,3}");
|
||||
|
||||
@Inject
|
||||
private GeoIpService geoIpService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "cty";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Check country protection / country data";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe country lookup");
|
||||
if (arguments.isEmpty()) {
|
||||
sender.sendMessage("Check player: /authme debug cty Bobby");
|
||||
sender.sendMessage("Check IP address: /authme debug cty 127.123.45.67");
|
||||
return;
|
||||
}
|
||||
|
||||
String argument = arguments.get(0);
|
||||
if (IS_IP_ADDR.matcher(argument).matches()) {
|
||||
outputInfoForIpAddr(sender, argument);
|
||||
} else {
|
||||
outputInfoForPlayer(sender, argument);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.COUNTRY_LOOKUP;
|
||||
}
|
||||
|
||||
private void outputInfoForIpAddr(CommandSender sender, String ipAddr) {
|
||||
sender.sendMessage("IP '" + ipAddr + "' maps to country '" + geoIpService.getCountryCode(ipAddr)
|
||||
+ "' (" + geoIpService.getCountryName(ipAddr) + ")");
|
||||
if (validationService.isCountryAdmitted(ipAddr)) {
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "This IP address' country is not blocked");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "This IP address' country is blocked from the server");
|
||||
}
|
||||
sender.sendMessage("Note: if " + ProtectionSettings.ENABLE_PROTECTION + " is false no country is blocked");
|
||||
}
|
||||
|
||||
// TODO #1366: Extend with registration IP?
|
||||
private void outputInfoForPlayer(CommandSender sender, String name) {
|
||||
PlayerAuth auth = dataSource.getAuth(name);
|
||||
if (auth == null) {
|
||||
sender.sendMessage("No player with name '" + name + "'");
|
||||
} else if (auth.getLastIp() == null) {
|
||||
sender.sendMessage("No last IP address known for '" + name + "'");
|
||||
} else {
|
||||
sender.sendMessage("Player '" + name + "' has IP address " + auth.getLastIp());
|
||||
outputInfoForIpAddr(sender, auth.getLastIp());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import ch.jalu.injector.factory.SingletonStore;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.datasource.CacheDataSource;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.initialization.HasCleanup;
|
||||
import fr.xephi.authme.initialization.Reloadable;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.applyToLimboPlayersMap;
|
||||
|
||||
/**
|
||||
* Fetches various statistics, particularly regarding in-memory data that is stored.
|
||||
*/
|
||||
class DataStatistics implements DebugSection {
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private LimboService limboService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private SingletonStore<Object> singletonStore;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "stats";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Outputs general data statistics";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe statistics");
|
||||
sender.sendMessage("LimboPlayers in memory: " + applyToLimboPlayersMap(limboService, Map::size));
|
||||
sender.sendMessage("PlayerCache size: " + playerCache.getLogged() + " (= logged in players)");
|
||||
|
||||
outputDatabaseStats(sender);
|
||||
outputInjectorStats(sender);
|
||||
sender.sendMessage("Total logger instances: " + ConsoleLoggerFactory.getTotalLoggers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.DATA_STATISTICS;
|
||||
}
|
||||
|
||||
private void outputDatabaseStats(CommandSender sender) {
|
||||
sender.sendMessage("Total players in DB: " + dataSource.getAccountsRegistered());
|
||||
if (dataSource instanceof CacheDataSource) {
|
||||
CacheDataSource cacheDataSource = (CacheDataSource) this.dataSource;
|
||||
sender.sendMessage("Cached PlayerAuth objects: " + cacheDataSource.getCachedAuths().size());
|
||||
}
|
||||
}
|
||||
|
||||
private void outputInjectorStats(CommandSender sender) {
|
||||
sender.sendMessage("Singleton Java classes: " + singletonStore.retrieveAllOfType().size());
|
||||
sender.sendMessage(String.format("(Reloadable: %d / SettingsDependent: %d / HasCleanup: %d)",
|
||||
singletonStore.retrieveAllOfType(Reloadable.class).size(),
|
||||
singletonStore.retrieveAllOfType(SettingsDependent.class).size(),
|
||||
singletonStore.retrieveAllOfType(HasCleanup.class).size()));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Debug command main.
|
||||
*/
|
||||
public class DebugCommand implements ExecutableCommand {
|
||||
|
||||
private static final Set<Class<? extends DebugSection>> SECTION_CLASSES = ImmutableSet.of(
|
||||
PermissionGroups.class, DataStatistics.class, CountryLookup.class, PlayerAuthViewer.class, InputValidator.class,
|
||||
LimboPlayerViewer.class, CountryLookup.class, HasPermissionChecker.class, TestEmailSender.class,
|
||||
SpawnLocationViewer.class, MySqlDefaultChanger.class);
|
||||
|
||||
@Inject
|
||||
private Factory<DebugSection> debugSectionFactory;
|
||||
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
|
||||
private Map<String, DebugSection> sections;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
DebugSection debugSection = findDebugSection(arguments);
|
||||
if (debugSection == null) {
|
||||
sendAvailableSections(sender);
|
||||
} else {
|
||||
executeSection(debugSection, sender, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
private DebugSection findDebugSection(List<String> arguments) {
|
||||
if (arguments.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return getSections().get(arguments.get(0).toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
private void sendAvailableSections(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe debug utils");
|
||||
sender.sendMessage("Sections available to you:");
|
||||
long availableSections = getSections().values().stream()
|
||||
.filter(section -> permissionsManager.hasPermission(sender, section.getRequiredPermission()))
|
||||
.peek(e -> sender.sendMessage("- " + e.getName() + ": " + e.getDescription()))
|
||||
.count();
|
||||
|
||||
if (availableSections == 0) {
|
||||
sender.sendMessage(ChatColor.RED + "You don't have permission to view any debug section");
|
||||
}
|
||||
}
|
||||
|
||||
private void executeSection(DebugSection section, CommandSender sender, List<String> arguments) {
|
||||
if (permissionsManager.hasPermission(sender, section.getRequiredPermission())) {
|
||||
section.execute(sender, arguments.subList(1, arguments.size()));
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "You don't have permission for this section. See /authme debug");
|
||||
}
|
||||
}
|
||||
|
||||
// Lazy getter
|
||||
private Map<String, DebugSection> getSections() {
|
||||
if (sections == null) {
|
||||
Map<String, DebugSection> sections = new TreeMap<>();
|
||||
for (Class<? extends DebugSection> sectionClass : SECTION_CLASSES) {
|
||||
DebugSection section = debugSectionFactory.newInstance(sectionClass);
|
||||
sections.put(section.getName(), section);
|
||||
}
|
||||
this.sections = sections;
|
||||
}
|
||||
return sections;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A debug section: "child" command of the debug command.
|
||||
*/
|
||||
interface DebugSection {
|
||||
|
||||
/**
|
||||
* @return the name to get to this child command
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* @return short description of the child command
|
||||
*/
|
||||
String getDescription();
|
||||
|
||||
/**
|
||||
* Executes the debug child command.
|
||||
*
|
||||
* @param sender the sender executing the command
|
||||
* @param arguments the arguments, without the label of the child command
|
||||
*/
|
||||
void execute(CommandSender sender, List<String> arguments);
|
||||
|
||||
/**
|
||||
* @return permission required to run this section
|
||||
*/
|
||||
PermissionNode getRequiredPermission();
|
||||
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.datasource.CacheDataSource;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Utilities used within the DebugSection implementations.
|
||||
*/
|
||||
final class DebugSectionUtils {
|
||||
|
||||
private static ConsoleLogger logger = ConsoleLoggerFactory.get(DebugSectionUtils.class);
|
||||
private static Field limboEntriesField;
|
||||
|
||||
private DebugSectionUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given location in a human readable way. Null-safe.
|
||||
*
|
||||
* @param location the location to format
|
||||
* @return the formatted location
|
||||
*/
|
||||
static String formatLocation(Location location) {
|
||||
if (location == null) {
|
||||
return "null";
|
||||
}
|
||||
|
||||
String worldName = location.getWorld() == null ? "null" : location.getWorld().getName();
|
||||
return formatLocation(location.getX(), location.getY(), location.getZ(), worldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given location in a human readable way.
|
||||
*
|
||||
* @param x the x coordinate
|
||||
* @param y the y coordinate
|
||||
* @param z the z coordinate
|
||||
* @param world the world name
|
||||
* @return the formatted location
|
||||
*/
|
||||
static String formatLocation(double x, double y, double z, String world) {
|
||||
return "(" + round(x) + ", " + round(y) + ", " + round(z) + ") in '" + world + "'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given number to two decimals.
|
||||
*
|
||||
* @param number the number to round
|
||||
* @return the rounded number
|
||||
*/
|
||||
private static String round(double number) {
|
||||
DecimalFormat df = new DecimalFormat("#.##");
|
||||
df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.US));
|
||||
df.setRoundingMode(RoundingMode.HALF_UP);
|
||||
return df.format(number);
|
||||
}
|
||||
|
||||
private static Field getLimboPlayerEntriesField() {
|
||||
if (limboEntriesField == null) {
|
||||
try {
|
||||
Field field = LimboService.class.getDeclaredField("entries");
|
||||
field.setAccessible(true);
|
||||
limboEntriesField = field;
|
||||
} catch (Exception e) {
|
||||
logger.logException("Could not retrieve LimboService entries field:", e);
|
||||
}
|
||||
}
|
||||
return limboEntriesField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given function to the map in LimboService containing the LimboPlayers.
|
||||
* As we don't want to expose this information in non-debug settings, this is done with reflection.
|
||||
* Exceptions are generously caught and {@code null} is returned on failure.
|
||||
*
|
||||
* @param limboService the limbo service instance to get the map from
|
||||
* @param function the function to apply to the map
|
||||
* @param <U> the result type of the function
|
||||
*
|
||||
* @return the value of the function applied to the map, or null upon error
|
||||
*/
|
||||
static <U> U applyToLimboPlayersMap(LimboService limboService, Function<Map, U> function) {
|
||||
Field limboPlayerEntriesField = getLimboPlayerEntriesField();
|
||||
if (limboPlayerEntriesField != null) {
|
||||
try {
|
||||
return function.apply((Map) limboEntriesField.get(limboService));
|
||||
} catch (Exception e) {
|
||||
logger.logException("Could not retrieve LimboService values:", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static <T> T castToTypeOrNull(Object object, Class<T> clazz) {
|
||||
return clazz.isInstance(object) ? clazz.cast(object) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps the "cache data source" and returns the underlying source. Returns the
|
||||
* same as the input argument otherwise.
|
||||
*
|
||||
* @param dataSource the data source to unwrap if applicable
|
||||
* @return the non-cache data source
|
||||
*/
|
||||
static DataSource unwrapSourceFromCacheDataSource(DataSource dataSource) {
|
||||
if (dataSource instanceof CacheDataSource) {
|
||||
try {
|
||||
Field source = CacheDataSource.class.getDeclaredField("source");
|
||||
source.setAccessible(true);
|
||||
return (DataSource) source.get(dataSource);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
logger.logException("Could not get source of CacheDataSource:", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return dataSource;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import fr.xephi.authme.permission.AdminPermission;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.DefaultPermission;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.permission.PlayerPermission;
|
||||
import fr.xephi.authme.permission.PlayerStatePermission;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* Checks if a player has a given permission, as checked by AuthMe.
|
||||
*/
|
||||
class HasPermissionChecker implements DebugSection {
|
||||
|
||||
static final List<Class<? extends PermissionNode>> PERMISSION_NODE_CLASSES = ImmutableList.of(
|
||||
AdminPermission.class, PlayerPermission.class, PlayerStatePermission.class, DebugSectionPermissions.class);
|
||||
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "perm";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Checks if a player has a given permission";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe permission check");
|
||||
if (arguments.size() < 2) {
|
||||
sender.sendMessage("Check if a player has permission:");
|
||||
sender.sendMessage("Example: /authme debug perm bobby my.perm.node");
|
||||
sender.sendMessage("Permission system type used: " + permissionsManager.getPermissionSystem());
|
||||
return;
|
||||
}
|
||||
|
||||
final String playerName = arguments.get(0);
|
||||
final String permissionNode = arguments.get(1);
|
||||
|
||||
Player player = bukkitService.getPlayerExact(playerName);
|
||||
if (player == null) {
|
||||
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerName);
|
||||
if (offlinePlayer == null) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Player '" + playerName + "' does not exist");
|
||||
} else {
|
||||
sender.sendMessage("Player '" + playerName + "' not online; checking with offline player");
|
||||
performPermissionCheck(offlinePlayer, permissionNode, permissionsManager::hasPermissionOffline, sender);
|
||||
}
|
||||
} else {
|
||||
performPermissionCheck(player, permissionNode, permissionsManager::hasPermission, sender);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.HAS_PERMISSION_CHECK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a permission check and informs the given sender of the result. {@code permissionChecker} is the
|
||||
* permission check to perform with the given {@code node} and the {@code player}.
|
||||
*
|
||||
* @param player the player to check a permission for
|
||||
* @param node the node of the permission to check
|
||||
* @param permissionChecker permission checking function
|
||||
* @param sender the sender to inform of the result
|
||||
* @param <P> the player type
|
||||
*/
|
||||
private static <P extends OfflinePlayer> void performPermissionCheck(
|
||||
P player, String node, BiFunction<P, PermissionNode, Boolean> permissionChecker, CommandSender sender) {
|
||||
|
||||
PermissionNode permNode = getPermissionNode(sender, node);
|
||||
if (permissionChecker.apply(player, permNode)) {
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "Success: player '" + player.getName()
|
||||
+ "' has permission '" + node + "'");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Check failed: player '" + player.getName()
|
||||
+ "' does NOT have permission '" + node + "'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the given permission node (String), tries to find the according AuthMe {@link PermissionNode}
|
||||
* instance, or creates a new one if not available.
|
||||
*
|
||||
* @param sender the sender (used to inform him if no AuthMe PermissionNode can be matched)
|
||||
* @param node the node to search for
|
||||
* @return the node as {@link PermissionNode} object
|
||||
*/
|
||||
private static PermissionNode getPermissionNode(CommandSender sender, String node) {
|
||||
Optional<? extends PermissionNode> permNode = PERMISSION_NODE_CLASSES.stream()
|
||||
.map(Class::getEnumConstants)
|
||||
.flatMap(Arrays::stream)
|
||||
.filter(perm -> perm.getNode().equals(node))
|
||||
.findFirst();
|
||||
if (permNode.isPresent()) {
|
||||
return permNode.get();
|
||||
} else {
|
||||
sender.sendMessage("Did not detect AuthMe permission; using default permission = DENIED");
|
||||
return createPermNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
private static PermissionNode createPermNode(String node) {
|
||||
return new PermissionNode() {
|
||||
@Override
|
||||
public String getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultPermission getDefaultPermission() {
|
||||
return DefaultPermission.NOT_ALLOWED;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,124 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.listener.FailedVerificationException;
|
||||
import fr.xephi.authme.listener.OnJoinVerifier;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.service.ValidationService.ValidationResult;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static fr.xephi.authme.command.executable.authme.debug.InputValidator.ValidationObject.MAIL;
|
||||
import static fr.xephi.authme.command.executable.authme.debug.InputValidator.ValidationObject.NAME;
|
||||
import static fr.xephi.authme.command.executable.authme.debug.InputValidator.ValidationObject.PASS;
|
||||
|
||||
/**
|
||||
* Checks if a sample username, email or password is valid according to the AuthMe settings.
|
||||
*/
|
||||
class InputValidator implements DebugSection {
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Inject
|
||||
private OnJoinVerifier onJoinVerifier;
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "valid";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Checks if your config.yml allows a password / email";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
if (arguments.size() < 2 || !ValidationObject.matchesAny(arguments.get(0))) {
|
||||
displayUsageHint(sender);
|
||||
|
||||
} else if (PASS.matches(arguments.get(0))) {
|
||||
validatePassword(sender, arguments.get(1));
|
||||
|
||||
} else if (MAIL.matches(arguments.get(0))) {
|
||||
validateEmail(sender, arguments.get(1));
|
||||
|
||||
} else if (NAME.matches(arguments.get(0))) {
|
||||
validateUsername(sender, arguments.get(1));
|
||||
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected validation object with arg[0] = '" + arguments.get(0) + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.INPUT_VALIDATOR;
|
||||
}
|
||||
|
||||
private void displayUsageHint(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.BLUE + "Validation tests");
|
||||
sender.sendMessage("You can define forbidden emails and passwords in your config.yml."
|
||||
+ " You can test your settings with this command.");
|
||||
final String command = ChatColor.GOLD + "/authme debug valid";
|
||||
sender.sendMessage(" Use " + command + " pass <pass>" + ChatColor.RESET + " to check a password");
|
||||
sender.sendMessage(" Use " + command + " mail <mail>" + ChatColor.RESET + " to check an email");
|
||||
sender.sendMessage(" Use " + command + " name <name>" + ChatColor.RESET + " to check a username");
|
||||
}
|
||||
|
||||
private void validatePassword(CommandSender sender, String password) {
|
||||
ValidationResult validationResult = validationService.validatePassword(password, "");
|
||||
sender.sendMessage(ChatColor.BLUE + "Validation of password '" + password + "'");
|
||||
if (validationResult.hasError()) {
|
||||
messages.send(sender, validationResult.getMessageKey(), validationResult.getArgs());
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "Valid password!");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateEmail(CommandSender sender, String email) {
|
||||
boolean isValidEmail = validationService.validateEmail(email);
|
||||
sender.sendMessage(ChatColor.BLUE + "Validation of email '" + email + "'");
|
||||
if (isValidEmail) {
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "Valid email!");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Email is not valid!");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateUsername(CommandSender sender, String username) {
|
||||
sender.sendMessage(ChatColor.BLUE + "Validation of username '" + username + "'");
|
||||
try {
|
||||
onJoinVerifier.checkIsValidName(username);
|
||||
sender.sendMessage("Valid username!");
|
||||
} catch (FailedVerificationException failedVerificationEx) {
|
||||
messages.send(sender, failedVerificationEx.getReason(), failedVerificationEx.getArgs());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum ValidationObject {
|
||||
|
||||
PASS, MAIL, NAME;
|
||||
|
||||
static boolean matchesAny(String arg) {
|
||||
return Arrays.stream(values()).anyMatch(vo -> vo.matches(arg));
|
||||
}
|
||||
|
||||
boolean matches(String arg) {
|
||||
return name().equalsIgnoreCase(arg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,143 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.data.limbo.LimboPlayer;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.data.limbo.persistence.LimboPersistence;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.applyToLimboPlayersMap;
|
||||
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.formatLocation;
|
||||
|
||||
/**
|
||||
* Shows the data stored in LimboPlayers and the equivalent properties on online players.
|
||||
*/
|
||||
class LimboPlayerViewer implements DebugSection {
|
||||
|
||||
@Inject
|
||||
private LimboService limboService;
|
||||
|
||||
@Inject
|
||||
private LimboPersistence limboPersistence;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "limbo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "View LimboPlayers and player's \"limbo stats\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
if (arguments.isEmpty()) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe limbo viewer");
|
||||
sender.sendMessage("/authme debug limbo <player>: show a player's limbo info");
|
||||
sender.sendMessage("Available limbo records: " + applyToLimboPlayersMap(limboService, Map::keySet));
|
||||
return;
|
||||
}
|
||||
|
||||
LimboPlayer memoryLimbo = limboService.getLimboPlayer(arguments.get(0));
|
||||
Player player = bukkitService.getPlayerExact(arguments.get(0));
|
||||
LimboPlayer diskLimbo = player != null ? limboPersistence.getLimboPlayer(player) : null;
|
||||
if (memoryLimbo == null && player == null) {
|
||||
sender.sendMessage(ChatColor.BLUE + "No AuthMe limbo data");
|
||||
sender.sendMessage("No limbo data and no player online with name '" + arguments.get(0) + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.BLUE + "Player / limbo / disk limbo info for '" + arguments.get(0) + "'");
|
||||
new InfoDisplayer(sender, player, memoryLimbo, diskLimbo)
|
||||
.sendEntry("Is op", Player::isOp, LimboPlayer::isOperator)
|
||||
.sendEntry("Walk speed", Player::getWalkSpeed, LimboPlayer::getWalkSpeed)
|
||||
.sendEntry("Can fly", Player::getAllowFlight, LimboPlayer::isCanFly)
|
||||
.sendEntry("Fly speed", Player::getFlySpeed, LimboPlayer::getFlySpeed)
|
||||
.sendEntry("Location", p -> formatLocation(p.getLocation()), l -> formatLocation(l.getLocation()))
|
||||
.sendEntry("Prim. group",
|
||||
p -> permissionsManager.hasGroupSupport() ? permissionsManager.getPrimaryGroup(p) : "N/A",
|
||||
LimboPlayer::getGroups);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.LIMBO_PLAYER_VIEWER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the info for the given LimboPlayer and Player to the provided CommandSender.
|
||||
*/
|
||||
private static final class InfoDisplayer {
|
||||
private final CommandSender sender;
|
||||
private final Optional<Player> player;
|
||||
private final Optional<LimboPlayer> memoryLimbo;
|
||||
private final Optional<LimboPlayer> diskLimbo;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param sender command sender to send the information to
|
||||
* @param player the player to get data from
|
||||
* @param memoryLimbo the limbo player to get data from
|
||||
*/
|
||||
InfoDisplayer(CommandSender sender, Player player, LimboPlayer memoryLimbo, LimboPlayer diskLimbo) {
|
||||
this.sender = sender;
|
||||
this.player = Optional.ofNullable(player);
|
||||
this.memoryLimbo = Optional.ofNullable(memoryLimbo);
|
||||
this.diskLimbo = Optional.ofNullable(diskLimbo);
|
||||
|
||||
if (memoryLimbo == null) {
|
||||
sender.sendMessage("Note: no Limbo information available");
|
||||
}
|
||||
if (player == null) {
|
||||
sender.sendMessage("Note: player is not online");
|
||||
} else if (diskLimbo == null) {
|
||||
sender.sendMessage("Note: no Limbo on disk available");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a piece of information to the command sender.
|
||||
*
|
||||
* @param title the designation of the piece of information
|
||||
* @param playerGetter getter for data retrieval on Player
|
||||
* @param limboGetter getter for data retrieval on the LimboPlayer
|
||||
* @param <T> the data type
|
||||
* @return this instance (for chaining)
|
||||
*/
|
||||
<T> InfoDisplayer sendEntry(String title,
|
||||
Function<Player, T> playerGetter,
|
||||
Function<LimboPlayer, T> limboGetter) {
|
||||
sender.sendMessage(
|
||||
title + ": "
|
||||
+ getData(player, playerGetter)
|
||||
+ " / "
|
||||
+ getData(memoryLimbo, limboGetter)
|
||||
+ " / "
|
||||
+ getData(diskLimbo, limboGetter));
|
||||
return this;
|
||||
}
|
||||
|
||||
static <E, T> String getData(Optional<E> entity, Function<E, T> getter) {
|
||||
return entity.map(getter).map(String::valueOf).orElse(" -- ");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,327 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import ch.jalu.configme.properties.Property;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.datasource.MySQL;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.castToTypeOrNull;
|
||||
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.unwrapSourceFromCacheDataSource;
|
||||
import static fr.xephi.authme.data.auth.PlayerAuth.DB_EMAIL_DEFAULT;
|
||||
import static fr.xephi.authme.data.auth.PlayerAuth.DB_LAST_IP_DEFAULT;
|
||||
import static fr.xephi.authme.data.auth.PlayerAuth.DB_LAST_LOGIN_DEFAULT;
|
||||
import static fr.xephi.authme.datasource.SqlDataSourceUtils.getColumnDefaultValue;
|
||||
import static fr.xephi.authme.datasource.SqlDataSourceUtils.isNotNullColumn;
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Convenience command to add or remove the default value of a column and its nullable status
|
||||
* in the MySQL data source.
|
||||
*/
|
||||
class MySqlDefaultChanger implements DebugSection {
|
||||
|
||||
private static final String NOT_NULL_SUFFIX = ChatColor.DARK_AQUA + "@" + ChatColor.RESET;
|
||||
private static final String DEFAULT_VALUE_SUFFIX = ChatColor.GOLD + "#" + ChatColor.RESET;
|
||||
|
||||
private ConsoleLogger logger = ConsoleLoggerFactory.get(MySqlDefaultChanger.class);
|
||||
|
||||
@Inject
|
||||
private Settings settings;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
private MySQL mySql;
|
||||
|
||||
@PostConstruct
|
||||
void setMySqlField() {
|
||||
this.mySql = castToTypeOrNull(unwrapSourceFromCacheDataSource(this.dataSource), MySQL.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "mysqldef";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Add or remove the default value of MySQL columns";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.MYSQL_DEFAULT_CHANGER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
if (mySql == null) {
|
||||
sender.sendMessage("Defaults can be changed for the MySQL data source only.");
|
||||
return;
|
||||
}
|
||||
|
||||
Operation operation = matchToEnum(arguments, 0, Operation.class);
|
||||
Columns column = matchToEnum(arguments, 1, Columns.class);
|
||||
if (operation == Operation.DETAILS) {
|
||||
showColumnDetails(sender);
|
||||
} else if (operation == null || column == null) {
|
||||
displayUsageHints(sender);
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.BLUE + "[AuthMe] MySQL change '" + column + "'");
|
||||
try (Connection con = getConnection(mySql)) {
|
||||
switch (operation) {
|
||||
case ADD:
|
||||
changeColumnToNotNullWithDefault(sender, column, con);
|
||||
break;
|
||||
case REMOVE:
|
||||
removeNotNullAndDefault(sender, column, con);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown operation '" + operation + "'");
|
||||
}
|
||||
} catch (SQLException | IllegalStateException e) {
|
||||
logger.logException("Failed to perform MySQL default altering operation:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a default value to the column definition and adds a {@code NOT NULL} constraint for
|
||||
* the specified column.
|
||||
*
|
||||
* @param sender the command sender initiation the action
|
||||
* @param column the column to modify
|
||||
* @param con connection to the database
|
||||
* @throws SQLException .
|
||||
*/
|
||||
private void changeColumnToNotNullWithDefault(CommandSender sender, Columns column,
|
||||
Connection con) throws SQLException {
|
||||
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
final String columnName = settings.getProperty(column.getColumnNameProperty());
|
||||
|
||||
// Replace NULLs with future default value
|
||||
String sql = format("UPDATE %s SET %s = ? WHERE %s IS NULL;", tableName, columnName, columnName);
|
||||
int updatedRows;
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setObject(1, column.getDefaultValue());
|
||||
updatedRows = pst.executeUpdate();
|
||||
}
|
||||
sender.sendMessage("Replaced NULLs with default value ('" + column.getDefaultValue()
|
||||
+ "'), modifying " + updatedRows + " entries");
|
||||
|
||||
// Change column definition to NOT NULL version
|
||||
try (Statement st = con.createStatement()) {
|
||||
st.execute(format("ALTER TABLE %s MODIFY %s %s", tableName, columnName, column.getNotNullDefinition()));
|
||||
sender.sendMessage("Changed column '" + columnName + "' to have NOT NULL constraint");
|
||||
}
|
||||
|
||||
// Log success message
|
||||
logger.info("Changed MySQL column '" + columnName + "' to be NOT NULL, as initiated by '"
|
||||
+ sender.getName() + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the {@code NOT NULL} constraint of a column definition and replaces rows with the
|
||||
* default value to {@code NULL}.
|
||||
*
|
||||
* @param sender the command sender initiation the action
|
||||
* @param column the column to modify
|
||||
* @param con connection to the database
|
||||
* @throws SQLException .
|
||||
*/
|
||||
private void removeNotNullAndDefault(CommandSender sender, Columns column, Connection con) throws SQLException {
|
||||
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
final String columnName = settings.getProperty(column.getColumnNameProperty());
|
||||
|
||||
// Change column definition to nullable version
|
||||
try (Statement st = con.createStatement()) {
|
||||
st.execute(format("ALTER TABLE %s MODIFY %s %s", tableName, columnName, column.getNullableDefinition()));
|
||||
sender.sendMessage("Changed column '" + columnName + "' to allow nulls");
|
||||
}
|
||||
|
||||
// Replace old default value with NULL
|
||||
String sql = format("UPDATE %s SET %s = NULL WHERE %s = ?;", tableName, columnName, columnName);
|
||||
int updatedRows;
|
||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||
pst.setObject(1, column.getDefaultValue());
|
||||
updatedRows = pst.executeUpdate();
|
||||
}
|
||||
sender.sendMessage("Replaced default value ('" + column.getDefaultValue()
|
||||
+ "') to be NULL, modifying " + updatedRows + " entries");
|
||||
|
||||
// Log success message
|
||||
logger.info("Changed MySQL column '" + columnName + "' to allow NULL, as initiated by '"
|
||||
+ sender.getName() + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the current definitions of all {@link Columns} which can be migrated.
|
||||
*
|
||||
* @param sender command sender to output the data to
|
||||
*/
|
||||
private void showColumnDetails(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.BLUE + "MySQL column details");
|
||||
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
try (Connection con = getConnection(mySql)) {
|
||||
final DatabaseMetaData metaData = con.getMetaData();
|
||||
for (Columns col : Columns.values()) {
|
||||
String columnName = settings.getProperty(col.getColumnNameProperty());
|
||||
String isNullText = isNotNullColumn(metaData, tableName, columnName) ? "NOT NULL" : "nullable";
|
||||
Object defaultValue = getColumnDefaultValue(metaData, tableName, columnName);
|
||||
String defaultText = defaultValue == null ? "no default" : "default: '" + defaultValue + "'";
|
||||
sender.sendMessage(formatColumnWithMetadata(col, metaData, tableName)
|
||||
+ " (" + columnName + "): " + isNullText + ", " + defaultText);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.logException("Failed while showing column details:", e);
|
||||
sender.sendMessage("Failed while showing column details. See log for info");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays sample commands and the list of columns that can be changed.
|
||||
*
|
||||
* @param sender the sender issuing the command
|
||||
*/
|
||||
private void displayUsageHints(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.BLUE + "MySQL column changer");
|
||||
sender.sendMessage("Adds or removes a NOT NULL constraint for a column.");
|
||||
sender.sendMessage("Examples: add a NOT NULL constraint with");
|
||||
sender.sendMessage(" /authme debug mysqldef add <column>");
|
||||
sender.sendMessage("Remove one with /authme debug mysqldef remove <column>");
|
||||
|
||||
sender.sendMessage("Available columns: " + constructColumnListWithMetadata());
|
||||
sender.sendMessage(" " + NOT_NULL_SUFFIX + ": not-null, " + DEFAULT_VALUE_SUFFIX
|
||||
+ ": has default. See /authme debug mysqldef details");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of {@link Columns} we can toggle with suffixes indicating their NOT NULL and default value status
|
||||
*/
|
||||
private String constructColumnListWithMetadata() {
|
||||
try (Connection con = getConnection(mySql)) {
|
||||
final DatabaseMetaData metaData = con.getMetaData();
|
||||
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||
|
||||
List<String> formattedColumns = new ArrayList<>(Columns.values().length);
|
||||
for (Columns col : Columns.values()) {
|
||||
formattedColumns.add(formatColumnWithMetadata(col, metaData, tableName));
|
||||
}
|
||||
return String.join(ChatColor.RESET + ", ", formattedColumns);
|
||||
} catch (SQLException e) {
|
||||
logger.logException("Failed to construct column list:", e);
|
||||
return ChatColor.RED + "An error occurred! Please see the console for details.";
|
||||
}
|
||||
}
|
||||
|
||||
private String formatColumnWithMetadata(Columns column, DatabaseMetaData metaData,
|
||||
String tableName) throws SQLException {
|
||||
String columnName = settings.getProperty(column.getColumnNameProperty());
|
||||
boolean isNotNull = isNotNullColumn(metaData, tableName, columnName);
|
||||
boolean hasDefaultValue = getColumnDefaultValue(metaData, tableName, columnName) != null;
|
||||
return column.name()
|
||||
+ (isNotNull ? NOT_NULL_SUFFIX : "")
|
||||
+ (hasDefaultValue ? DEFAULT_VALUE_SUFFIX : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Connection object from the MySQL data source.
|
||||
*
|
||||
* @param mySql the MySQL data source to get the connection from
|
||||
* @return the connection
|
||||
*/
|
||||
@VisibleForTesting
|
||||
Connection getConnection(MySQL mySql) {
|
||||
try {
|
||||
Method method = MySQL.class.getDeclaredMethod("getConnection");
|
||||
method.setAccessible(true);
|
||||
return (Connection) method.invoke(mySql);
|
||||
} catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
|
||||
throw new IllegalStateException("Could not get MySQL connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static <E extends Enum<E>> E matchToEnum(List<String> arguments, int index, Class<E> clazz) {
|
||||
if (arguments.size() <= index) {
|
||||
return null;
|
||||
}
|
||||
String str = arguments.get(index);
|
||||
return Arrays.stream(clazz.getEnumConstants())
|
||||
.filter(e -> e.name().equalsIgnoreCase(str))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private enum Operation {
|
||||
ADD, REMOVE, DETAILS
|
||||
}
|
||||
|
||||
/** MySQL columns which can be toggled between being NOT NULL and allowing NULL values. */
|
||||
enum Columns {
|
||||
|
||||
LASTLOGIN(DatabaseSettings.MYSQL_COL_LASTLOGIN,
|
||||
"BIGINT", "BIGINT NOT NULL DEFAULT 0", DB_LAST_LOGIN_DEFAULT),
|
||||
|
||||
LASTIP(DatabaseSettings.MYSQL_COL_LAST_IP,
|
||||
"VARCHAR(40) CHARACTER SET ascii COLLATE ascii_bin",
|
||||
"VARCHAR(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL DEFAULT '127.0.0.1'",
|
||||
DB_LAST_IP_DEFAULT),
|
||||
|
||||
EMAIL(DatabaseSettings.MYSQL_COL_EMAIL,
|
||||
"VARCHAR(255)", "VARCHAR(255) NOT NULL DEFAULT 'your@email.com'", DB_EMAIL_DEFAULT);
|
||||
|
||||
private final Property<String> columnNameProperty;
|
||||
private final String nullableDefinition;
|
||||
private final String notNullDefinition;
|
||||
private final Object defaultValue;
|
||||
|
||||
Columns(Property<String> columnNameProperty, String nullableDefinition,
|
||||
String notNullDefinition, Object defaultValue) {
|
||||
this.columnNameProperty = columnNameProperty;
|
||||
this.nullableDefinition = nullableDefinition;
|
||||
this.notNullDefinition = notNullDefinition;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
/** @return property defining the column name in the database */
|
||||
Property<String> getColumnNameProperty() {
|
||||
return columnNameProperty;
|
||||
}
|
||||
|
||||
/** @return SQL definition of the column allowing NULL values */
|
||||
String getNullableDefinition() {
|
||||
return nullableDefinition;
|
||||
}
|
||||
|
||||
/** @return SQL definition of the column with a NOT NULL constraint */
|
||||
String getNotNullDefinition() {
|
||||
return notNullDefinition;
|
||||
}
|
||||
|
||||
/** @return the default value used in {@link #notNullDefinition} */
|
||||
Object getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
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;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
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.
|
||||
*/
|
||||
class PermissionGroups implements DebugSection {
|
||||
|
||||
@Inject
|
||||
private PermissionsManager permissionsManager;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "groups";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Show permission groups a player belongs to";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe permission groups");
|
||||
String name = arguments.isEmpty() ? sender.getName() : arguments.get(0);
|
||||
Player player = Bukkit.getPlayer(name);
|
||||
if (player == null) {
|
||||
sender.sendMessage("Player " + name + " could not be found");
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.PERM_GROUPS;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,117 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.formatLocation;
|
||||
|
||||
/**
|
||||
* Allows to view the data of a PlayerAuth in the database.
|
||||
*/
|
||||
class PlayerAuthViewer implements DebugSection {
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "db";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "View player's data in the database";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
if (arguments.isEmpty()) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe database viewer");
|
||||
sender.sendMessage("Enter player name to view his data in the database.");
|
||||
sender.sendMessage("Example: /authme debug db Bobby");
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerAuth auth = dataSource.getAuth(arguments.get(0));
|
||||
if (auth == null) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe database viewer");
|
||||
sender.sendMessage("No record exists for '" + arguments.get(0) + "'");
|
||||
} else {
|
||||
displayAuthToSender(auth, sender);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.PLAYER_AUTH_VIEWER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the PlayerAuth information to the given sender.
|
||||
*
|
||||
* @param auth the PlayerAuth to display
|
||||
* @param sender the sender to send the messages to
|
||||
*/
|
||||
private void displayAuthToSender(PlayerAuth auth, CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.BLUE + "[AuthMe] Player " + auth.getNickname() + " / " + auth.getRealName());
|
||||
sender.sendMessage("Email: " + auth.getEmail() + ". IP: " + auth.getLastIp() + ". Group: " + auth.getGroupId());
|
||||
sender.sendMessage("Quit location: "
|
||||
+ formatLocation(auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ(), auth.getWorld()));
|
||||
sender.sendMessage("Last login: " + formatDate(auth.getLastLogin()));
|
||||
sender.sendMessage("Registration: " + formatDate(auth.getRegistrationDate())
|
||||
+ " with IP " + auth.getRegistrationIp());
|
||||
|
||||
HashedPassword hashedPass = auth.getPassword();
|
||||
sender.sendMessage("Hash / salt (partial): '" + safeSubstring(hashedPass.getHash(), 6)
|
||||
+ "' / '" + safeSubstring(hashedPass.getSalt(), 4) + "'");
|
||||
sender.sendMessage("TOTP code (partial): '" + safeSubstring(auth.getTotpKey(), 3) + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail-safe substring method. Guarantees not to show the entire String.
|
||||
*
|
||||
* @param str the string to transform
|
||||
* @param length number of characters to show from the start of the String
|
||||
* @return the first <code>length</code> characters of the string, or half of the string if it is shorter,
|
||||
* or empty string if the string is null or empty
|
||||
*/
|
||||
private static String safeSubstring(String str, int length) {
|
||||
if (StringUtils.isBlank(str)) {
|
||||
return "";
|
||||
} else if (str.length() < length) {
|
||||
return str.substring(0, str.length() / 2) + "...";
|
||||
} else {
|
||||
return str.substring(0, length) + "...";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given timestamp to a human readable date.
|
||||
*
|
||||
* @param timestamp the timestamp to format (nullable)
|
||||
* @return the formatted timestamp
|
||||
*/
|
||||
private static String formatDate(Long timestamp) {
|
||||
if (timestamp == null) {
|
||||
return "Not available (null)";
|
||||
} else if (timestamp == 0) {
|
||||
return "Not available (0)";
|
||||
} else {
|
||||
LocalDateTime date = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
|
||||
return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(date);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.SpawnLoader;
|
||||
import fr.xephi.authme.settings.properties.RestrictionSettings;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.formatLocation;
|
||||
|
||||
/**
|
||||
* Shows the spawn location that AuthMe is configured to use.
|
||||
*/
|
||||
class SpawnLocationViewer implements DebugSection {
|
||||
|
||||
@Inject
|
||||
private SpawnLoader spawnLoader;
|
||||
|
||||
@Inject
|
||||
private Settings settings;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "spawn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Shows the spawn location that AuthMe will use";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe spawn location viewer");
|
||||
if (arguments.isEmpty()) {
|
||||
showGeneralInfo(sender);
|
||||
} else if ("?".equals(arguments.get(0))) {
|
||||
showHelp(sender);
|
||||
} else {
|
||||
showPlayerSpawn(sender, arguments.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.SPAWN_LOCATION;
|
||||
}
|
||||
|
||||
private void showGeneralInfo(CommandSender sender) {
|
||||
sender.sendMessage("Spawn priority: "
|
||||
+ String.join(", ", settings.getProperty(RestrictionSettings.SPAWN_PRIORITY)));
|
||||
sender.sendMessage("AuthMe spawn location: " + formatLocation(spawnLoader.getSpawn()));
|
||||
sender.sendMessage("AuthMe first spawn location: " + formatLocation(spawnLoader.getFirstSpawn()));
|
||||
sender.sendMessage("AuthMe (first)spawn are only used depending on the configured priority!");
|
||||
sender.sendMessage("Use '/authme debug spawn ?' for further help");
|
||||
}
|
||||
|
||||
private void showHelp(CommandSender sender) {
|
||||
sender.sendMessage("Use /authme spawn and /authme firstspawn to teleport to the spawns.");
|
||||
sender.sendMessage("/authme set(first)spawn sets the (first) spawn to your current location.");
|
||||
sender.sendMessage("Use /authme debug spawn <player> to view where a player would be teleported to.");
|
||||
sender.sendMessage("Read more at https://github.com/AuthMe/AuthMeReloaded/wiki/Spawn-Handling");
|
||||
}
|
||||
|
||||
private void showPlayerSpawn(CommandSender sender, String playerName) {
|
||||
Player player = bukkitService.getPlayerExact(playerName);
|
||||
if (player == null) {
|
||||
sender.sendMessage("Player '" + playerName + "' is not online!");
|
||||
} else {
|
||||
Location spawn = spawnLoader.getSpawnLocation(player);
|
||||
sender.sendMessage("Player '" + playerName + "' has spawn location: " + formatLocation(spawn));
|
||||
sender.sendMessage("Note: this check excludes the AuthMe firstspawn.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.mail.SendMailSsl;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
import org.apache.commons.mail.EmailException;
|
||||
import org.apache.commons.mail.HtmlEmail;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sends out a test email.
|
||||
*/
|
||||
class TestEmailSender implements DebugSection {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(TestEmailSender.class);
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private SendMailSsl sendMailSsl;
|
||||
|
||||
@Inject
|
||||
private Server server;
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "mail";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Sends out a test email";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, List<String> arguments) {
|
||||
sender.sendMessage(ChatColor.BLUE + "AuthMe test email sender");
|
||||
if (!sendMailSsl.hasAllInformation()) {
|
||||
sender.sendMessage(ChatColor.RED + "You haven't set all required configurations in config.yml "
|
||||
+ "for sending emails. Please check your config.yml");
|
||||
return;
|
||||
}
|
||||
|
||||
String email = getEmail(sender, arguments);
|
||||
|
||||
// getEmail() takes care of informing the sender of the error if email == null
|
||||
if (email != null) {
|
||||
boolean sendMail = sendTestEmail(email);
|
||||
if (sendMail) {
|
||||
sender.sendMessage("Test email sent to " + email + " with success");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "Failed to send test mail to " + email + "; please check your logs");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionNode getRequiredPermission() {
|
||||
return DebugSectionPermissions.TEST_EMAIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the email address to use based on the sender and the arguments. If the arguments are empty,
|
||||
* we attempt to retrieve the email from the sender. If there is an argument, we verify that it is
|
||||
* an email address.
|
||||
* {@code null} is returned if no email address could be found. This method informs the sender of
|
||||
* the specific error in such cases.
|
||||
*
|
||||
* @param sender the command sender
|
||||
* @param arguments the provided arguments
|
||||
* @return the email to use, or null if none found
|
||||
*/
|
||||
private String getEmail(CommandSender sender, List<String> arguments) {
|
||||
if (arguments.isEmpty()) {
|
||||
DataSourceValue<String> emailResult = dataSource.getEmail(sender.getName());
|
||||
if (!emailResult.rowExists()) {
|
||||
sender.sendMessage(ChatColor.RED + "Please provide an email address, "
|
||||
+ "e.g. /authme debug mail test@example.com");
|
||||
return null;
|
||||
}
|
||||
final String email = emailResult.getValue();
|
||||
if (Utils.isEmailEmpty(email)) {
|
||||
sender.sendMessage(ChatColor.RED + "No email set for your account!"
|
||||
+ " Please use /authme debug mail <email>");
|
||||
return null;
|
||||
}
|
||||
return email;
|
||||
} else {
|
||||
String email = arguments.get(0);
|
||||
if (StringUtils.isInsideString('@', email)) {
|
||||
return email;
|
||||
}
|
||||
sender.sendMessage(ChatColor.RED + "Invalid email! Usage: /authme debug mail test@example.com");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean sendTestEmail(String email) {
|
||||
HtmlEmail htmlEmail;
|
||||
try {
|
||||
htmlEmail = sendMailSsl.initializeMail(email);
|
||||
} catch (EmailException e) {
|
||||
logger.logException("Failed to create email for sample email:", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
htmlEmail.setSubject("AuthMe test email");
|
||||
String message = "Hello there!<br />This is a sample email sent to you from a Minecraft server ("
|
||||
+ server.getName() + ") via /authme debug mail. If you're seeing this, sending emails should be fine.";
|
||||
return sendMailSsl.sendEmail(message, htmlEmail);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
package fr.xephi.authme.command.executable.captcha;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.data.captcha.LoginCaptchaManager;
|
||||
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
|
||||
import fr.xephi.authme.data.limbo.LimboMessageType;
|
||||
import fr.xephi.authme.data.limbo.LimboService;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Captcha command, allowing a player to solve a captcha.
|
||||
*/
|
||||
public class CaptchaCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private LoginCaptchaManager loginCaptchaManager;
|
||||
|
||||
@Inject
|
||||
private RegistrationCaptchaManager registrationCaptchaManager;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private LimboService limboService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
final String name = player.getName();
|
||||
|
||||
if (playerCache.isAuthenticated(name)) {
|
||||
// No captcha is relevant if the player is logged in
|
||||
commonService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (loginCaptchaManager.isCaptchaRequired(name)) {
|
||||
checkLoginCaptcha(player, arguments.get(0));
|
||||
} else {
|
||||
final boolean isPlayerRegistered = dataSource.isAuthAvailable(name);
|
||||
if (!isPlayerRegistered && registrationCaptchaManager.isCaptchaRequired(name)) {
|
||||
checkRegisterCaptcha(player, arguments.get(0));
|
||||
} else {
|
||||
MessageKey errorMessage = isPlayerRegistered ? MessageKey.USAGE_LOGIN : MessageKey.USAGE_REGISTER;
|
||||
commonService.send(player, errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkLoginCaptcha(Player player, String captchaCode) {
|
||||
final boolean isCorrectCode = loginCaptchaManager.checkCode(player, captchaCode);
|
||||
if (isCorrectCode) {
|
||||
commonService.send(player, MessageKey.CAPTCHA_SUCCESS);
|
||||
commonService.send(player, MessageKey.LOGIN_MESSAGE);
|
||||
limboService.unmuteMessageTask(player);
|
||||
} else {
|
||||
String newCode = loginCaptchaManager.getCaptchaCodeOrGenerateNew(player.getName());
|
||||
commonService.send(player, MessageKey.CAPTCHA_WRONG_ERROR, newCode);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRegisterCaptcha(Player player, String captchaCode) {
|
||||
final boolean isCorrectCode = registrationCaptchaManager.checkCode(player, captchaCode);
|
||||
if (isCorrectCode) {
|
||||
commonService.send(player, MessageKey.REGISTER_CAPTCHA_SUCCESS);
|
||||
commonService.send(player, MessageKey.REGISTER_MESSAGE);
|
||||
} else {
|
||||
String newCode = registrationCaptchaManager.getCaptchaCodeOrGenerateNew(player.getName());
|
||||
commonService.send(player, MessageKey.CAPTCHA_WRONG_ERROR, newCode);
|
||||
}
|
||||
limboService.resetMessageTask(player, LimboMessageType.REGISTER);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package fr.xephi.authme.command.executable.changepassword;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.VerificationCodeManager;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.service.ValidationService.ValidationResult;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* The command for a player to change his password with.
|
||||
*/
|
||||
public class ChangePasswordCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Inject
|
||||
private VerificationCodeManager codeManager;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
String name = player.getName().toLowerCase(Locale.ROOT);
|
||||
|
||||
if (!playerCache.isAuthenticated(name)) {
|
||||
commonService.send(player, MessageKey.NOT_LOGGED_IN);
|
||||
return;
|
||||
}
|
||||
// Check if the user has been verified or not
|
||||
if (codeManager.isVerificationRequired(player)) {
|
||||
codeManager.codeExistOrGenerateNew(name);
|
||||
commonService.send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
|
||||
return;
|
||||
}
|
||||
|
||||
String oldPassword = arguments.get(0);
|
||||
String newPassword = arguments.get(1);
|
||||
|
||||
// Make sure the password is allowed
|
||||
ValidationResult passwordValidation = validationService.validatePassword(newPassword, name);
|
||||
if (passwordValidation.hasError()) {
|
||||
commonService.send(player, passwordValidation.getMessageKey(), passwordValidation.getArgs());
|
||||
return;
|
||||
}
|
||||
|
||||
management.performPasswordChange(player, oldPassword, newPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAlternativeCommand() {
|
||||
return "/authme password <playername> <password>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getArgumentsMismatchMessage() {
|
||||
return MessageKey.USAGE_CHANGE_PASSWORD;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for setting an email to an account.
|
||||
*/
|
||||
public class AddEmailCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
String email = arguments.get(0);
|
||||
String emailConfirmation = arguments.get(1);
|
||||
|
||||
if (email.equals(emailConfirmation)) {
|
||||
// Closer inspection of the mail address handled by the async task
|
||||
management.performAddEmail(player, email);
|
||||
} else {
|
||||
commonService.send(player, MessageKey.CONFIRM_EMAIL_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getArgumentsMismatchMessage() {
|
||||
return MessageKey.USAGE_ADD_EMAIL;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.VerificationCodeManager;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Change email command.
|
||||
*/
|
||||
public class ChangeEmailCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private VerificationCodeManager codeManager;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
final String playerName = player.getName();
|
||||
// Check if the user has been verified or not
|
||||
if (codeManager.isVerificationRequired(player)) {
|
||||
codeManager.codeExistOrGenerateNew(playerName);
|
||||
commonService.send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
|
||||
return;
|
||||
}
|
||||
|
||||
String playerMailOld = arguments.get(0);
|
||||
String playerMailNew = arguments.get(1);
|
||||
management.performChangeEmail(player, playerMailOld, playerMailNew);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getArgumentsMismatchMessage() {
|
||||
return MessageKey.USAGE_CHANGE_EMAIL;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import fr.xephi.authme.command.CommandMapper;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.command.FoundCommandResult;
|
||||
import fr.xephi.authme.command.help.HelpProvider;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Base command for /email, showing information about the child commands.
|
||||
*/
|
||||
public class EmailBaseCommand implements ExecutableCommand {
|
||||
|
||||
@Inject
|
||||
private CommandMapper commandMapper;
|
||||
|
||||
@Inject
|
||||
private HelpProvider helpProvider;
|
||||
|
||||
@Override
|
||||
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||
FoundCommandResult result = commandMapper.mapPartsToCommand(sender, Collections.singletonList("email"));
|
||||
helpProvider.outputHelp(sender, result, HelpProvider.SHOW_CHILDREN);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.security.PasswordSecurity;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.PasswordRecoveryService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.service.ValidationService.ValidationResult;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for changing password following successful recovery.
|
||||
*/
|
||||
public class EmailSetPasswordCommand extends PlayerCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(EmailSetPasswordCommand.class);
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private PasswordRecoveryService recoveryService;
|
||||
|
||||
@Inject
|
||||
private PasswordSecurity passwordSecurity;
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
if (recoveryService.canChangePassword(player)) {
|
||||
String name = player.getName();
|
||||
String password = arguments.get(0);
|
||||
|
||||
ValidationResult result = validationService.validatePassword(password, name);
|
||||
if (!result.hasError()) {
|
||||
HashedPassword hashedPassword = passwordSecurity.computeHash(password, name);
|
||||
dataSource.updatePassword(name, hashedPassword);
|
||||
recoveryService.removeFromSuccessfulRecovery(player);
|
||||
logger.info("Player '" + name + "' has changed their password from recovery");
|
||||
commonService.send(player, MessageKey.PASSWORD_CHANGED_SUCCESS);
|
||||
} else {
|
||||
commonService.send(player, result.getMessageKey(), result.getArgs());
|
||||
}
|
||||
} else {
|
||||
commonService.send(player, MessageKey.CHANGE_PASSWORD_EXPIRED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.PasswordRecoveryService;
|
||||
import fr.xephi.authme.service.RecoveryCodeService;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for submitting email recovery code.
|
||||
*/
|
||||
public class ProcessCodeCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private RecoveryCodeService codeService;
|
||||
|
||||
@Inject
|
||||
private PasswordRecoveryService recoveryService;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
String name = player.getName();
|
||||
String code = arguments.get(0);
|
||||
|
||||
if (codeService.hasTriesLeft(name)) {
|
||||
if (codeService.isCodeValid(name, code)) {
|
||||
commonService.send(player, MessageKey.RECOVERY_CODE_CORRECT);
|
||||
recoveryService.addSuccessfulRecovery(player);
|
||||
codeService.removeCode(name);
|
||||
} else {
|
||||
commonService.send(player, MessageKey.INCORRECT_RECOVERY_CODE,
|
||||
Integer.toString(codeService.getTriesLeft(name)));
|
||||
}
|
||||
} else {
|
||||
codeService.removeCode(name);
|
||||
commonService.send(player, MessageKey.RECOVERY_TRIES_EXCEEDED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import ch.jalu.datasourcecolumns.data.DataSourceValue;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.mail.EmailService;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.PasswordRecoveryService;
|
||||
import fr.xephi.authme.service.RecoveryCodeService;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for password recovery by email.
|
||||
*/
|
||||
public class RecoverEmailCommand extends PlayerCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(RecoverEmailCommand.class);
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private EmailService emailService;
|
||||
|
||||
@Inject
|
||||
private PasswordRecoveryService recoveryService;
|
||||
|
||||
@Inject
|
||||
private RecoveryCodeService recoveryCodeService;
|
||||
|
||||
@Inject
|
||||
private BukkitService bukkitService;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
final String playerMail = arguments.get(0);
|
||||
final String playerName = player.getName();
|
||||
|
||||
if (!emailService.hasAllInformation()) {
|
||||
logger.warning("Mail API is not set");
|
||||
commonService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
|
||||
return;
|
||||
}
|
||||
if (playerCache.isAuthenticated(playerName)) {
|
||||
commonService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
DataSourceValue<String> emailResult = dataSource.getEmail(playerName);
|
||||
if (!emailResult.rowExists()) {
|
||||
commonService.send(player, MessageKey.USAGE_REGISTER);
|
||||
return;
|
||||
}
|
||||
|
||||
final String email = emailResult.getValue();
|
||||
if (Utils.isEmailEmpty(email) || !email.equalsIgnoreCase(playerMail)) {
|
||||
commonService.send(player, MessageKey.INVALID_EMAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
bukkitService.runTaskAsynchronously(() -> {
|
||||
if (recoveryCodeService.isRecoveryCodeNeeded()) {
|
||||
// Recovery code is needed; generate and send one
|
||||
recoveryService.createAndSendRecoveryCode(player, email);
|
||||
} else {
|
||||
// Code not needed, just send them a new password
|
||||
recoveryService.generateAndSendNewPassword(player, email);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getArgumentsMismatchMessage() {
|
||||
return MessageKey.USAGE_RECOVER_EMAIL;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package fr.xephi.authme.command.executable.email;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||
import fr.xephi.authme.util.Utils;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Show email command.
|
||||
*/
|
||||
public class ShowEmailCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
PlayerAuth auth = playerCache.getAuth(player.getName());
|
||||
if (auth != null && !Utils.isEmailEmpty(auth.getEmail())) {
|
||||
if (commonService.getProperty(SecuritySettings.USE_EMAIL_MASKING)){
|
||||
commonService.send(player, MessageKey.EMAIL_SHOW, emailMask(auth.getEmail()));
|
||||
} else {
|
||||
commonService.send(player, MessageKey.EMAIL_SHOW, auth.getEmail());
|
||||
}
|
||||
} else {
|
||||
commonService.send(player, MessageKey.SHOW_NO_EMAIL);
|
||||
}
|
||||
}
|
||||
|
||||
private String emailMask(String email){
|
||||
String[] frag = email.split("@"); //Split id and domain
|
||||
int sid = frag[0].length() / 3 + 1; //Define the id view (required length >= 1)
|
||||
int sdomain = frag[1].length() / 3; //Define the domain view (required length >= 0)
|
||||
String id = frag[0].substring(0, sid) + "***"; //Build the id
|
||||
String domain = "***" + frag[1].substring(sdomain); //Build the domain
|
||||
return id + "@" + domain;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package fr.xephi.authme.command.executable.login;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Login command.
|
||||
*/
|
||||
public class LoginCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
String password = arguments.get(0);
|
||||
management.performLogin(player, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getArgumentsMismatchMessage() {
|
||||
return MessageKey.USAGE_LOGIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAlternativeCommand() {
|
||||
return "/authme forcelogin <player>";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package fr.xephi.authme.command.executable.logout;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Logout command.
|
||||
*/
|
||||
public class LogoutCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
management.performLogout(player);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,225 @@
|
||||
package fr.xephi.authme.command.executable.register;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.mail.EmailService;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.process.register.RegisterSecondaryArgument;
|
||||
import fr.xephi.authme.process.register.RegistrationType;
|
||||
import fr.xephi.authme.process.register.executors.EmailRegisterParams;
|
||||
import fr.xephi.authme.process.register.executors.PasswordRegisterParams;
|
||||
import fr.xephi.authme.process.register.executors.RegistrationMethod;
|
||||
import fr.xephi.authme.process.register.executors.TwoFactorRegisterParams;
|
||||
import fr.xephi.authme.security.HashAlgorithm;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.service.ValidationService;
|
||||
import fr.xephi.authme.settings.properties.EmailSettings;
|
||||
import fr.xephi.authme.settings.properties.RegistrationSettings;
|
||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import static fr.xephi.authme.process.register.RegisterSecondaryArgument.CONFIRMATION;
|
||||
import static fr.xephi.authme.process.register.RegisterSecondaryArgument.EMAIL_MANDATORY;
|
||||
import static fr.xephi.authme.process.register.RegisterSecondaryArgument.EMAIL_OPTIONAL;
|
||||
import static fr.xephi.authme.process.register.RegisterSecondaryArgument.NONE;
|
||||
import static fr.xephi.authme.settings.properties.RegistrationSettings.REGISTER_SECOND_ARGUMENT;
|
||||
|
||||
/**
|
||||
* Command for /register.
|
||||
*/
|
||||
public class RegisterCommand extends PlayerCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(RegisterCommand.class);
|
||||
|
||||
@Inject
|
||||
private Management management;
|
||||
|
||||
@Inject
|
||||
private CommonService commonService;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private EmailService emailService;
|
||||
|
||||
@Inject
|
||||
private ValidationService validationService;
|
||||
|
||||
@Inject
|
||||
private RegistrationCaptchaManager registrationCaptchaManager;
|
||||
|
||||
@Override
|
||||
public void runCommand(Player player, List<String> arguments) {
|
||||
if (!isCaptchaFulfilled(player)) {
|
||||
return; // isCaptchaFulfilled handles informing the player on failure
|
||||
}
|
||||
|
||||
if (commonService.getProperty(SecuritySettings.PASSWORD_HASH) == HashAlgorithm.TWO_FACTOR) {
|
||||
//for two factor auth we don't need to check the usage
|
||||
management.performRegister(RegistrationMethod.TWO_FACTOR_REGISTRATION,
|
||||
TwoFactorRegisterParams.of(player));
|
||||
return;
|
||||
} else if (arguments.size() < 1) {
|
||||
commonService.send(player, MessageKey.USAGE_REGISTER);
|
||||
return;
|
||||
}
|
||||
|
||||
RegistrationType registrationType = commonService.getProperty(RegistrationSettings.REGISTRATION_TYPE);
|
||||
if (registrationType == RegistrationType.PASSWORD) {
|
||||
handlePasswordRegistration(player, arguments);
|
||||
} else if (registrationType == RegistrationType.EMAIL) {
|
||||
handleEmailRegistration(player, arguments);
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown registration type '" + registrationType + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAlternativeCommand() {
|
||||
return "/authme register <playername> <password>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getArgumentsMismatchMessage() {
|
||||
return MessageKey.USAGE_REGISTER;
|
||||
}
|
||||
|
||||
private boolean isCaptchaFulfilled(Player player) {
|
||||
if (registrationCaptchaManager.isCaptchaRequired(player.getName())) {
|
||||
String code = registrationCaptchaManager.getCaptchaCodeOrGenerateNew(player.getName());
|
||||
commonService.send(player, MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, code);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handlePasswordRegistration(Player player, List<String> arguments) {
|
||||
if (isSecondArgValidForPasswordRegistration(player, arguments)) {
|
||||
final String password = arguments.get(0);
|
||||
final String email = getEmailIfAvailable(arguments);
|
||||
|
||||
management.performRegister(RegistrationMethod.PASSWORD_REGISTRATION,
|
||||
PasswordRegisterParams.of(player, password, email));
|
||||
}
|
||||
}
|
||||
|
||||
private String getEmailIfAvailable(List<String> arguments) {
|
||||
if (arguments.size() >= 2) {
|
||||
RegisterSecondaryArgument secondArgType = commonService.getProperty(REGISTER_SECOND_ARGUMENT);
|
||||
if (secondArgType == EMAIL_MANDATORY || secondArgType == EMAIL_OPTIONAL) {
|
||||
return arguments.get(1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the second argument is valid (based on the configuration)
|
||||
* to perform a password registration. The player is informed if the check
|
||||
* is unsuccessful.
|
||||
*
|
||||
* @param player the player to register
|
||||
* @param arguments the provided arguments
|
||||
* @return true if valid, false otherwise
|
||||
*/
|
||||
private boolean isSecondArgValidForPasswordRegistration(Player player, List<String> arguments) {
|
||||
RegisterSecondaryArgument secondArgType = commonService.getProperty(REGISTER_SECOND_ARGUMENT);
|
||||
// cases where args.size < 2
|
||||
if (secondArgType == NONE || secondArgType == EMAIL_OPTIONAL && arguments.size() < 2) {
|
||||
return true;
|
||||
} else if (arguments.size() < 2) {
|
||||
commonService.send(player, MessageKey.USAGE_REGISTER);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (secondArgType == CONFIRMATION) {
|
||||
if (arguments.get(0).equals(arguments.get(1))) {
|
||||
return true;
|
||||
} else {
|
||||
commonService.send(player, MessageKey.PASSWORD_MATCH_ERROR);
|
||||
return false;
|
||||
}
|
||||
} else if (secondArgType == EMAIL_MANDATORY || secondArgType == EMAIL_OPTIONAL) {
|
||||
if (validationService.validateEmail(arguments.get(1))) {
|
||||
return true;
|
||||
} else {
|
||||
commonService.send(player, MessageKey.INVALID_EMAIL);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown secondary argument type '" + secondArgType + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEmailRegistration(Player player, List<String> arguments) {
|
||||
if (!emailService.hasAllInformation()) {
|
||||
commonService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
|
||||
logger.warning("Cannot register player '" + player.getName() + "': no email or password is set "
|
||||
+ "to send emails from. Please adjust your config at " + EmailSettings.MAIL_ACCOUNT.getPath());
|
||||
return;
|
||||
}
|
||||
|
||||
final String email = arguments.get(0);
|
||||
if (!validationService.validateEmail(email)) {
|
||||
commonService.send(player, MessageKey.INVALID_EMAIL);
|
||||
} else if (isSecondArgValidForEmailRegistration(player, arguments)) {
|
||||
management.performRegister(RegistrationMethod.EMAIL_REGISTRATION,
|
||||
EmailRegisterParams.of(player, email));
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (dataSource.getAuth(player.getName()) != null) {
|
||||
if (dataSource.getAuth(player.getName()).getLastLogin() == null) {
|
||||
management.performUnregisterByAdmin(null, player.getName(), player);
|
||||
timer.cancel();
|
||||
}
|
||||
} else {
|
||||
timer.cancel();
|
||||
}
|
||||
}
|
||||
}, 600000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the second argument is valid (based on the configuration)
|
||||
* to perform an email registration. The player is informed if the check
|
||||
* is unsuccessful.
|
||||
*
|
||||
* @param player the player to register
|
||||
* @param arguments the provided arguments
|
||||
* @return true if valid, false otherwise
|
||||
*/
|
||||
private boolean isSecondArgValidForEmailRegistration(Player player, List<String> arguments) {
|
||||
RegisterSecondaryArgument secondArgType = commonService.getProperty(REGISTER_SECOND_ARGUMENT);
|
||||
// cases where args.size < 2
|
||||
if (secondArgType == NONE || secondArgType == EMAIL_OPTIONAL && arguments.size() < 2) {
|
||||
return true;
|
||||
} else if (arguments.size() < 2) {
|
||||
commonService.send(player, MessageKey.USAGE_REGISTER);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (secondArgType == EMAIL_OPTIONAL || secondArgType == EMAIL_MANDATORY || secondArgType == CONFIRMATION) {
|
||||
if (arguments.get(0).equals(arguments.get(1))) {
|
||||
return true;
|
||||
} else {
|
||||
commonService.send(player, MessageKey.USAGE_REGISTER);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown secondary argument type '" + secondArgType + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package fr.xephi.authme.command.executable.totp;
|
||||
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.security.totp.GenerateTotpService;
|
||||
import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for a player to enable TOTP.
|
||||
*/
|
||||
public class AddTotpCommand extends PlayerCommand {
|
||||
|
||||
@Inject
|
||||
private GenerateTotpService generateTotpService;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
PlayerAuth auth = playerCache.getAuth(player.getName());
|
||||
if (auth == null) {
|
||||
messages.send(player, MessageKey.NOT_LOGGED_IN);
|
||||
} else if (auth.getTotpKey() == null) {
|
||||
TotpGenerationResult createdTotpInfo = generateTotpService.generateTotpKey(player);
|
||||
messages.send(player, MessageKey.TWO_FACTOR_CREATE,
|
||||
createdTotpInfo.getTotpKey(), createdTotpInfo.getAuthenticatorQrCodeUrl());
|
||||
messages.send(player, MessageKey.TWO_FACTOR_CREATE_CONFIRMATION_REQUIRED);
|
||||
} else {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package fr.xephi.authme.command.executable.totp;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.security.totp.GenerateTotpService;
|
||||
import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command to enable TOTP by supplying the proper code as confirmation.
|
||||
*/
|
||||
public class ConfirmTotpCommand extends PlayerCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(ConfirmTotpCommand.class);
|
||||
|
||||
@Inject
|
||||
private GenerateTotpService generateTotpService;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
PlayerAuth auth = playerCache.getAuth(player.getName());
|
||||
if (auth == null) {
|
||||
messages.send(player, MessageKey.NOT_LOGGED_IN);
|
||||
} else if (auth.getTotpKey() != null) {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED);
|
||||
} else {
|
||||
verifyTotpCodeConfirmation(player, auth, arguments.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyTotpCodeConfirmation(Player player, PlayerAuth auth, String inputTotpCode) {
|
||||
final TotpGenerationResult totpDetails = generateTotpService.getGeneratedTotpKey(player);
|
||||
if (totpDetails == null) {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_NO_CODE);
|
||||
} else {
|
||||
boolean isCodeValid = generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, inputTotpCode);
|
||||
if (isCodeValid) {
|
||||
generateTotpService.removeGenerateTotpKey(player);
|
||||
insertTotpKeyIntoDatabase(player, auth, totpDetails);
|
||||
} else {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_WRONG_CODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void insertTotpKeyIntoDatabase(Player player, PlayerAuth auth, TotpGenerationResult totpDetails) {
|
||||
if (dataSource.setTotpKey(player.getName(), totpDetails.getTotpKey())) {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_ENABLE_SUCCESS);
|
||||
auth.setTotpKey(totpDetails.getTotpKey());
|
||||
playerCache.updatePlayer(auth);
|
||||
logger.info("Player '" + player.getName() + "' has successfully added a TOTP key to their account");
|
||||
} else {
|
||||
messages.send(player, MessageKey.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
package fr.xephi.authme.command.executable.totp;
|
||||
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.PlayerCommand;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.message.Messages;
|
||||
import fr.xephi.authme.output.ConsoleLoggerFactory;
|
||||
import fr.xephi.authme.security.totp.TotpAuthenticator;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command for a player to remove 2FA authentication.
|
||||
*/
|
||||
public class RemoveTotpCommand extends PlayerCommand {
|
||||
|
||||
private final ConsoleLogger logger = ConsoleLoggerFactory.get(RemoveTotpCommand.class);
|
||||
|
||||
@Inject
|
||||
private DataSource dataSource;
|
||||
|
||||
@Inject
|
||||
private PlayerCache playerCache;
|
||||
|
||||
@Inject
|
||||
private TotpAuthenticator totpAuthenticator;
|
||||
|
||||
@Inject
|
||||
private Messages messages;
|
||||
|
||||
@Override
|
||||
protected void runCommand(Player player, List<String> arguments) {
|
||||
PlayerAuth auth = playerCache.getAuth(player.getName());
|
||||
if (auth == null) {
|
||||
messages.send(player, MessageKey.NOT_LOGGED_IN);
|
||||
} else if (auth.getTotpKey() == null) {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_NOT_ENABLED_ERROR);
|
||||
} else {
|
||||
if (totpAuthenticator.checkCode(auth, arguments.get(0))) {
|
||||
removeTotpKeyFromDatabase(player, auth);
|
||||
} else {
|
||||
messages.send(player, MessageKey.TWO_FACTOR_INVALID_CODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeTotpKeyFromDatabase(Player player, PlayerAuth auth) {
|
||||
if (dataSource.removeTotpKey(auth.getNickname())) {
|
||||
auth.setTotpKey(null);
|
||||
playerCache.updatePlayer(auth);
|
||||
messages.send(player, MessageKey.TWO_FACTOR_REMOVED_SUCCESS);
|
||||
logger.info("Player '" + player.getName() + "' removed their TOTP key");
|
||||
} else {
|
||||
messages.send(player, MessageKey.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user