forked from TuxCoding/FastLogin
Compare commits
5 Commits
add-postgr
...
session
Author | SHA1 | Date | |
---|---|---|---|
c21c7eaeec | |||
804a0aec4b | |||
13bc92f6c3 | |||
e88f2e7f8e | |||
337e01e537 |
41
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
41
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Something isn't working
|
||||||
|
title: ''
|
||||||
|
labels: 'bug'
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[//]: # (Lines in this format are considered as comments and will not be displayed.)
|
||||||
|
[//]: # (Before reporting make sure you're running the **latest build** of the plugin and checked for existing 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. Please explain it in detail)
|
||||||
|
|
||||||
|
### Screenshots (if applicable)
|
||||||
|
[//]: # (You can drop the files here directly)
|
||||||
|
|
||||||
|
### Plugin list:
|
||||||
|
[//]: # (This can be found by running `/pl`)
|
||||||
|
|
||||||
|
### Environment description
|
||||||
|
[//]: # (Server software with exact version number, Minecraft version, SQLite/MySQL/MariaDB, ...)
|
||||||
|
|
||||||
|
### Plugin version or build number (don't write latest):
|
||||||
|
[//]: # (This can be found by running `/version plugin-name`.)
|
||||||
|
|
||||||
|
### Server Log:
|
||||||
|
[//]: # (No images please - only the textual representation)
|
||||||
|
[Hastebin](https://hastebin.com/) / [Gist](https://gist.github.com/) link of the error, stacktrace or the complete log (if any)
|
||||||
|
|
||||||
|
### Configuration:
|
||||||
|
[//]: # (No images please - only the textual representation)
|
||||||
|
[//]: # (remember to delete any sensitive data)
|
||||||
|
[Hastebin](https://hastebin.com/) / [Gist](https://gist.github.com/) link of your config.yml file
|
69
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
69
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -1,69 +0,0 @@
|
|||||||
name: 🐞 Bug Report
|
|
||||||
description: Something isn't working, broken, not expected behavior
|
|
||||||
labels: [ bug ]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
This ticket is about bugs, so broken, not expected behavior. Feedback about this form is appreciated.
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: What happened?
|
|
||||||
description: What behavior is observed?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: What did you expect?
|
|
||||||
description: What behavior is expected?
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Steps to reproduce
|
|
||||||
description: The actions that cause the issues. Please explain it in detail.
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: Plugin list
|
|
||||||
description: This can be found by running `/pl`
|
|
||||||
placeholder: AuthMe, ProtocolLib, ...
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: Configuration file
|
|
||||||
description: |
|
|
||||||
Link to the contents of your config.yml file.
|
|
||||||
You can use [GitHub](https://gist.github.com/), [Hastebin](https://hastebin.com) or similar for that.
|
|
||||||
placeholder: https://gist.github.com/games647/88c4439e1cd7810f21318b1b24a04ee0
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Server log
|
|
||||||
description: The error, stacktrace or link the complete log. You can use the links above for long versions.
|
|
||||||
placeholder: https://www.toptal.com/developers/hastebin / https://gist.github.com/
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: Plugin version
|
|
||||||
description: Plugin version or build number. This can be found by running `/version plugin-name`
|
|
||||||
placeholder: v3.1-SNAPSHOT-570b321
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Platform
|
|
||||||
description: Server software - choose your proxy software if you have multiple servers
|
|
||||||
options:
|
|
||||||
- Spigot
|
|
||||||
- BungeeCord
|
|
||||||
- Velocity
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Relevance
|
|
||||||
description: Check list for previous tickets
|
|
||||||
options:
|
|
||||||
- label: |
|
|
||||||
I tried the [latest build](https://ci.codemc.io/job/Games647/job/FastLogin/)
|
|
||||||
(build refers to development builds not necessary a release version; i.e. v1.10 is out of date)
|
|
||||||
required: true
|
|
||||||
- label: |
|
|
||||||
I checked for existing tickets -
|
|
||||||
If there are, please vote them with a thumbs reaction and not create new ones
|
|
||||||
required: true
|
|
12
.github/ISSUE_TEMPLATE/config.yml
vendored
12
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,12 +0,0 @@
|
|||||||
# General configuration for issue templates
|
|
||||||
|
|
||||||
# Allow issues without a template
|
|
||||||
#blank_issues_enabled: false
|
|
||||||
|
|
||||||
# Extra section on creating issues to redirect to another site
|
|
||||||
contact_links:
|
|
||||||
- name: 📌 Questions
|
|
||||||
url: https://github.com/games647/FastLogin/discussions
|
|
||||||
about:
|
|
||||||
You want to ask something - general questions. Example includes how to set it up or how it is working internally
|
|
||||||
|
|
22
.github/ISSUE_TEMPLATE/enhancement_request.md
vendored
Normal file
22
.github/ISSUE_TEMPLATE/enhancement_request.md
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
name: Enhancement request
|
||||||
|
about: New feature or change request
|
||||||
|
title: ''
|
||||||
|
labels: 'enhancement'
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[//]: # (Lines in this format are considered as comments and will not be displayed.)
|
||||||
|
|
||||||
|
### Is your feature request related to a problem? Please describe.
|
||||||
|
[//]: # (A clear and concise description of what the problem is. Ex. I'm always frustrated when [...])
|
||||||
|
|
||||||
|
### Describe the solution you'd like
|
||||||
|
[//]: # (A clear and concise description of what you want to happen.)
|
||||||
|
|
||||||
|
### Describe alternatives you've considered
|
||||||
|
[//]: # (A clear and concise description of any alternative solutions or features you've considered.)
|
||||||
|
|
||||||
|
### Additional context
|
||||||
|
[//]: # (Add any other context or screenshots about the feature request here.)
|
43
.github/ISSUE_TEMPLATE/enhancement_request.yaml
vendored
43
.github/ISSUE_TEMPLATE/enhancement_request.yaml
vendored
@ -1,43 +0,0 @@
|
|||||||
name: 💡 Enhancement request
|
|
||||||
description: New feature or change request
|
|
||||||
labels: [ enhancement ]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
This ticket is about suggestions for a feature or particular enhancement.
|
|
||||||
Feedback about this form is appreciated.
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Is your feature request related to a problem? Please describe.
|
|
||||||
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Describe the solution you'd like
|
|
||||||
description: A clear and concise description of what you want to happen.
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Describe alternatives you've considered
|
|
||||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Platform
|
|
||||||
description: Server software - choose your proxy software if you have multiple servers
|
|
||||||
options:
|
|
||||||
- Spigot
|
|
||||||
- BungeeCord
|
|
||||||
- Velocity
|
|
||||||
- All / Shared
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Relevance
|
|
||||||
description: Check list for previous tickets
|
|
||||||
options:
|
|
||||||
- label: |
|
|
||||||
I checked for existing tickets -
|
|
||||||
If there are, please vote them with a thumbs reaction and not create new ones
|
|
||||||
required: true
|
|
10
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
name: Question
|
||||||
|
about: You want to ask something
|
||||||
|
title: ''
|
||||||
|
labels: 'question'
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
35
.github/dependabot.yml
vendored
35
.github/dependabot.yml
vendored
@ -1,35 +0,0 @@
|
|||||||
version: 2
|
|
||||||
|
|
||||||
updates:
|
|
||||||
# Updates for workflow files
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
# Workflow files stored in the
|
|
||||||
# default location of `.github/workflows`
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "monthly"
|
|
||||||
|
|
||||||
# Maven project
|
|
||||||
- package-ecosystem: maven
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
|
|
||||||
groups:
|
|
||||||
production-dependencies:
|
|
||||||
dependency-type: "production"
|
|
||||||
development-dependencies:
|
|
||||||
dependency-type: "development"
|
|
||||||
exclude-patterns:
|
|
||||||
# Create single PR for these
|
|
||||||
# Plugin require special evaluation about their compatibility
|
|
||||||
- "com.lenis0012.bukkit:loginsecurity"
|
|
||||||
- "com.comphenix.protocol:ProtocolLib"
|
|
||||||
|
|
||||||
ignore:
|
|
||||||
# HikariCP dropped Java 8 support with 5.0
|
|
||||||
- dependency-name: "com.zaxxer:HikariCP"
|
|
||||||
update-types: ["version-update:semver-major"]
|
|
||||||
# SnakeYAML has breaking changes with 2.0
|
|
||||||
- dependency-name: "org.yaml:snakeyaml"
|
|
||||||
update-types: ["version-update:semver-major"]
|
|
5
.github/pull_request_template.md
vendored
5
.github/pull_request_template.md
vendored
@ -1,11 +1,8 @@
|
|||||||
[//]: # (Lines in this format are considered as comments and will not be displayed.)
|
[//]: # (Lines in this format are considered as comments and will not be displayed.)
|
||||||
|
|
||||||
[//]: # (If your work is in progress, please consider making a draft pull request.)
|
[//]: # (If your work is in progress, please consider making a draft pull request.)
|
||||||
|
|
||||||
### Summary of your change
|
### Summary of your change
|
||||||
|
[//]: # (Example: motiviation, enhancement)
|
||||||
[//]: # (Example: motivation, enhancement)
|
|
||||||
|
|
||||||
### Related issue
|
### Related issue
|
||||||
|
|
||||||
[//]: # (Reference it using '#NUMBER'. Ex: Fixes/Related #...)
|
[//]: # (Reference it using '#NUMBER'. Ex: Fixes/Related #...)
|
||||||
|
65
.github/workflows/codeql-analysis.yml
vendored
65
.github/workflows/codeql-analysis.yml
vendored
@ -1,65 +0,0 @@
|
|||||||
# GitHub automatic code security scanning using CodeQL
|
|
||||||
|
|
||||||
# Human-readable name in the actions tab
|
|
||||||
name: "CodeQL"
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_run:
|
|
||||||
workflows: ["Maven Build"]
|
|
||||||
branches: [main]
|
|
||||||
types:
|
|
||||||
- completed
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# job i
|
|
||||||
analyze:
|
|
||||||
|
|
||||||
# Display name
|
|
||||||
name: Analyze
|
|
||||||
|
|
||||||
# Environment
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
|
|
||||||
|
|
||||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: true
|
|
||||||
matrix:
|
|
||||||
# Languages to scan
|
|
||||||
language: [ 'java' ]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v3
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
|
|
||||||
# Setup Java
|
|
||||||
- name: Set up JDK
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version-file: '.java-version'
|
|
||||||
cache: 'maven'
|
|
||||||
|
|
||||||
# Manually start the autobuild process, because autobuild always selects Java 8 as build toolchain, but
|
|
||||||
# we are doing cross-crompiling from a newer Java version
|
|
||||||
- name: Build with Maven
|
|
||||||
# Extracted from autobuild
|
|
||||||
run: mvn package -f "pom.xml" --batch-mode -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip -DskipTests -Dmaven.test.skip.exec -Dlicense.skip=true -Drat.skip=true -Dspotless.check.skip=true -t /home/runner/.m2/toolchains.xml
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v3
|
|
||||||
with:
|
|
||||||
category: "/language:${{matrix.language}}"
|
|
60
.github/workflows/maven.yml
vendored
60
.github/workflows/maven.yml
vendored
@ -1,47 +1,49 @@
|
|||||||
# Automatically build, run unit and integration tests to detect errors early (CI provided by GitHub)
|
# Automatically build, run unit and integration tests to detect errors early (CI provided by GitHub)
|
||||||
# including making pull requests review easier
|
# including making pull requests review easier
|
||||||
|
|
||||||
# Human-readable name in the actions tab
|
# Human readable name in the actions tab
|
||||||
name: Maven Build
|
name: Java CI
|
||||||
|
|
||||||
# Build on every pull request regardless of the branch
|
# Build on every push and pull request regardless of the branch
|
||||||
# Wiki: https://help.github.com/en/actions/reference/events-that-trigger-workflows
|
# Wiki: https://help.github.com/en/actions/reference/events-that-trigger-workflows
|
||||||
on:
|
on:
|
||||||
push:
|
- push
|
||||||
branches:
|
- pull_request
|
||||||
- main
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# job id
|
# job id
|
||||||
build_and_test:
|
build_and_test:
|
||||||
|
|
||||||
# Environment image - always use the newest OS
|
# Environment image - always newest OS
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
# Run steps
|
# Run steps
|
||||||
steps:
|
steps:
|
||||||
# Pull changes
|
# Pull changes
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2.3.4
|
||||||
|
|
||||||
# Setup Java
|
# Cache artifacts - however this has the downside that we don't get notified of
|
||||||
- name: Set up JDK
|
# artifact resolution failures like invalid repository
|
||||||
uses: actions/setup-java@v4
|
# Nevertheless the repositories should be more stable and it makes no sense to pull
|
||||||
with:
|
# a same version every time
|
||||||
distribution: 'temurin'
|
# A dry run would make more sense
|
||||||
java-version-file: '.java-version'
|
- uses: actions/cache@v2.1.4
|
||||||
cache: 'maven'
|
with:
|
||||||
|
path: ~/.m2/repository
|
||||||
|
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-maven-
|
||||||
|
|
||||||
# Build and test (included in package)
|
# Setup Java
|
||||||
- name: Build with Maven and test
|
- name: Set up JDK
|
||||||
# Run non-interactive, package (with compile+test),
|
uses: actions/setup-java@v1.4.3
|
||||||
# ignore snapshot updates, because they are likely to have breaking changes, enforce checksums
|
with:
|
||||||
run: mvn test --batch-mode --threads 2.0C --no-snapshot-updates --strict-checksums --file pom.xml
|
# Use Java 11, because it's minimum required version
|
||||||
|
java-version: 11
|
||||||
|
|
||||||
- name: Update dependency graph
|
# Build and test (included in package)
|
||||||
if: ${{ github.event_name == 'push' }}
|
- name: Build with Maven and test
|
||||||
uses: advanced-security/maven-dependency-submission-action@v4.0.0
|
# Run non-interactive, package (with compile+test),
|
||||||
|
# ignore snapshot updates, because they are likely to have breaking changes, enforce checksums to validate
|
||||||
|
# possible errors in dependencies
|
||||||
|
run: mvn package test --batch-mode --no-snapshot-updates --strict-checksums --file pom.xml
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -13,9 +13,6 @@ nb-configuration.xml
|
|||||||
*.iws
|
*.iws
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
# VSCode
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
# Maven
|
# Maven
|
||||||
target/
|
target/
|
||||||
pom.xml.versionsBackup
|
pom.xml.versionsBackup
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
# Latest GitHub Action java release that is installed on the runners
|
|
||||||
21
|
|
@ -35,7 +35,7 @@
|
|||||||
* Automatically register accounts if they are not in the auth plugin database but in the FastLogin database
|
* Automatically register accounts if they are not in the auth plugin database but in the FastLogin database
|
||||||
* Update BungeeAuth dependency and use the new API. Please update your plugin if you still use the old one.
|
* Update BungeeAuth dependency and use the new API. Please update your plugin if you still use the old one.
|
||||||
* Remove deprecated API methods from the last version
|
* Remove deprecated API methods from the last version
|
||||||
* Finally, update the IP column on every login
|
* Finally update the IP column on every login
|
||||||
* No duplicate session login
|
* No duplicate session login
|
||||||
* Fix timestamp parsing in newer versions of SQLite
|
* Fix timestamp parsing in newer versions of SQLite
|
||||||
* Fix Spigot console command invocation sends result to in game players
|
* Fix Spigot console command invocation sends result to in game players
|
||||||
@ -82,7 +82,7 @@
|
|||||||
* Fix player entry is not saved if namechangecheck is enabled
|
* Fix player entry is not saved if namechangecheck is enabled
|
||||||
* Fix skin applies for third-party plugins
|
* Fix skin applies for third-party plugins
|
||||||
* Switch to mcapi.ca for uuid lookups
|
* Switch to mcapi.ca for uuid lookups
|
||||||
* Fix BungeeCord not setting a premium uuid
|
* Fix BungeeCord not setting an premium uuid
|
||||||
* Fix setting skin on Cauldron
|
* Fix setting skin on Cauldron
|
||||||
* Fix saving on name change
|
* Fix saving on name change
|
||||||
|
|
||||||
@ -148,7 +148,7 @@
|
|||||||
### 1.2
|
### 1.2
|
||||||
|
|
||||||
* Fix race condition in BungeeCord
|
* Fix race condition in BungeeCord
|
||||||
* Fix deadlock in xAuth
|
* Fix dead lock in xAuth
|
||||||
* Added API methods for plugins to set their own password generator
|
* Added API methods for plugins to set their own password generator
|
||||||
* Added API methods for plugins to set their own auth plugin hook
|
* Added API methods for plugins to set their own auth plugin hook
|
||||||
=> Added support for AdvancedLogin
|
=> Added support for AdvancedLogin
|
||||||
@ -182,7 +182,7 @@
|
|||||||
* Added a forwardSkin config option
|
* Added a forwardSkin config option
|
||||||
* Added premium UUID support
|
* Added premium UUID support
|
||||||
* Updated to the newest changes of Spigot
|
* Updated to the newest changes of Spigot
|
||||||
* Removes the need of a Bukkit auth plugin if you use a bungeecord one
|
* Removes the need of an Bukkit auth plugin if you use a bungeecord one
|
||||||
* Optimize performance and thread-safety
|
* Optimize performance and thread-safety
|
||||||
* Fixed BungeeCord support
|
* Fixed BungeeCord support
|
||||||
* Changed config option auto-login to auto-register to clarify the usage
|
* Changed config option auto-login to auto-register to clarify the usage
|
||||||
|
3
LICENSE
3
LICENSE
@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2015-2023 games647 and contributors
|
Copyright (c) 2015-2018
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
78
README.md
78
README.md
@ -12,28 +12,32 @@ So they don't need to enter passwords. This is also called auto login (auto-logi
|
|||||||
* Forge/Sponge message support
|
* Forge/Sponge message support
|
||||||
* Premium UUID support
|
* Premium UUID support
|
||||||
* Forward skins
|
* Forward skins
|
||||||
* Detect username changed and will update the existing database record
|
* Detect user name changed and will update the existing database record
|
||||||
* BungeeCord support
|
* BungeeCord support
|
||||||
* Auto register new premium players
|
* Auto register new premium players
|
||||||
* Plugin: ProtocolSupport is supported and can be used as an alternative to ProtocolLib
|
* Plugin: ProtocolSupport is supported and can be used as an alternative to ProtocolLib
|
||||||
* No client modifications needed
|
* No client modifications needed
|
||||||
* Good performance by using async operations
|
* Good performance by using async operations
|
||||||
* Locale messages
|
* Locale messages
|
||||||
* Support for Bedrock players proxies through FloodGate
|
* Support for Bedrock players proxied through FloodGate
|
||||||
|
|
||||||
## Issues
|
## Issues
|
||||||
|
|
||||||
Please use issues for bug reports, suggestions, questions and more. Please check for existing issues. Existing issues
|
Please use issues for bug reports, suggestions, questions and more. Please check for existing issues. Existing issues
|
||||||
can be voted up by adding up vote to the original post. Closing issues means that they are marked as resolved. Comments
|
can be voted up by adding up vote to the original post. Closing issues means that they are marked as resolved. Comments
|
||||||
are still allowed and it could be re-opened.
|
are still allowed and it could be re-opened.
|
||||||
|
|
||||||
## Development builds
|
## Development builds
|
||||||
|
|
||||||
Development builds contain the latest changes from the Source-Code. They are bleeding edge and could introduce new bugs,
|
Development builds of this project can be acquired at the provided CI (continuous integration) server. It contains the
|
||||||
but also include features, enhancements and bug fixes that are not yet in a released version. If you click on the left
|
latest changes from the Source-Code in preparation for the following release. This means they could contain new
|
||||||
side on `Changes`, you can see iterative change sets leading to a specific build.
|
features, bug fixes and other changes since the last release.
|
||||||
|
|
||||||
You can download them from here: https://ci.codemc.org/job/Games647/job/FastLogin/
|
They **could** contain new bugs and are likely to be less stable than released versions.
|
||||||
|
|
||||||
|
Specific builds can be grabbed by clicking on the build number on the left side or by clicking on status to retrieve the
|
||||||
|
latest build.
|
||||||
|
https://ci.codemc.org/job/Games647/job/FastLogin/changes
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
@ -51,22 +55,21 @@ You can download them from here: https://ci.codemc.org/job/Games647/job/FastLogi
|
|||||||
|
|
||||||
## Placeholder
|
## Placeholder
|
||||||
|
|
||||||
This plugin supports `PlaceholderAPI` on `Spigot`. It exports the following variable
|
This plugin supports `PlaceholderAPI` on `Spigot`. It exports the following variable
|
||||||
`%fastlogin_status%`. In BungeeCord environments, the status of a player will be delivered with a delay after the player
|
`%fastlogin_status%`. In BungeeCord environments, the status of a player will be delivered with a delay after the player
|
||||||
already successful joined the server. This takes about a couple of milliseconds. In this case the value
|
already successful joined the server. This takes about a couple of milliseconds. In this case the value
|
||||||
will be `Unknown`.
|
will be `Unknown`.
|
||||||
|
|
||||||
Possible values: `Premium`, `Cracked`, `Unknown`
|
Possible values: `Premium`, `Cracked`, `Unknown`
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
* Java 17+ (Recommended)
|
* Plugin:
|
||||||
* Server software in offlinemode:
|
* [ProtocolLib](https://www.spigotmc.org/resources/protocollib.1997/) or
|
||||||
* Spigot (or a fork e.g. Paper) 1.8.8+
|
* [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/)
|
||||||
* Protocol plugin:
|
* [Spigot](https://www.spigotmc.org) 1.8.8+
|
||||||
* [ProtocolLib 5.1+](https://www.spigotmc.org/resources/protocollib.1997/) or
|
* Java 8+
|
||||||
* [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/)
|
* Run Spigot (or a fork e.g. Paper) and/or BungeeCord (or a fork e.g. Waterfall) in offline mode
|
||||||
* Latest BungeeCord (or a fork e.g. Waterfall) or Velocity
|
|
||||||
* An auth plugin.
|
* An auth plugin.
|
||||||
|
|
||||||
### Supported auth plugins
|
### Supported auth plugins
|
||||||
@ -78,6 +81,7 @@ Possible values: `Premium`, `Cracked`, `Unknown`
|
|||||||
* [CrazyLogin](https://dev.bukkit.org/bukkit-plugins/crazylogin/)
|
* [CrazyLogin](https://dev.bukkit.org/bukkit-plugins/crazylogin/)
|
||||||
* [LoginSecurity](https://dev.bukkit.org/bukkit-plugins/loginsecurity/)
|
* [LoginSecurity](https://dev.bukkit.org/bukkit-plugins/loginsecurity/)
|
||||||
* [LogIt](https://github.com/games647/LogIt)
|
* [LogIt](https://github.com/games647/LogIt)
|
||||||
|
* [SodionAuth (2.0+)](https://github.com/Mohist-Community/SodionAuth)
|
||||||
* [UltraAuth](https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/)
|
* [UltraAuth](https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/)
|
||||||
* [UserLogin](https://www.spigotmc.org/resources/userlogin.80669/)
|
* [UserLogin](https://www.spigotmc.org/resources/userlogin.80669/)
|
||||||
* [xAuth](https://dev.bukkit.org/bukkit-plugins/xauth/)
|
* [xAuth](https://dev.bukkit.org/bukkit-plugins/xauth/)
|
||||||
@ -101,28 +105,20 @@ This plugin performs network requests to:
|
|||||||
### Spigot/Paper
|
### Spigot/Paper
|
||||||
|
|
||||||
1. Download and install ProtocolLib/ProtocolSupport
|
1. Download and install ProtocolLib/ProtocolSupport
|
||||||
2. Download and install FastLogin (or `FastLoginBukkit` for newer versions)
|
2. Download and install FastLogin (or FastLoginBukkit for newer versions)
|
||||||
3. Set your server in offline mode by setting the value `onlinemode` in your server.properties to false
|
3. Set your server in offline mode by setting the value onlinemode in your server.properties to false
|
||||||
|
|
||||||
### BungeeCord/Waterfall or Velocity
|
### BungeeCord/Waterfall
|
||||||
|
|
||||||
Install the plugin on both platforms, that is proxy (BungeeCord or Velocity) and backend server (Spigot).
|
1. Activate BungeeCord in the Spigot configuration
|
||||||
|
2. Restart your server
|
||||||
1. Activate proxy support in the server configuration
|
3. Now there is `allowed-proxies.txt` file in the FastLogin folder
|
||||||
* This is often found in `spigot.yml` or `paper.yml`
|
Put your stats id from the BungeeCord config into this file
|
||||||
2. Restart the backend server
|
4. Activate ipForward in your BungeeCord config
|
||||||
3. Now there is `allowed-proxies.txt` file in the FastLogin folder of the restarted server
|
5. Download and Install FastLogin (or FastLoginBungee in newer versions) on BungeeCord AND Spigot
|
||||||
* BungeeCord: Put your `stats`-id from the BungeeCord config into this file
|
(on the servers where your login plugin is or where player should be able to execute the commands of FastLogin)
|
||||||
* Velocity: On plugin startup the plugin generates a `proxyId.txt` inside the plugins folder of the proxy
|
6. Check your database settings in the config of FastLogin on BungeeCord
|
||||||
4. Activate ip forwarding in your proxy config
|
7. Set proxy and Spigot in offline mode by setting the value onlinemode in your config.yml to false
|
||||||
5. Check your database settings in the config of FastLogin on your proxy
|
8. You should *always* firewall your Spigot server that it's only accessible through BungeeCord
|
||||||
* The proxies only ship with a limited set of drivers where Spigot supports more. Therefore, these are supported:
|
* https://www.spigotmc.org/wiki/bungeecord-installation/#post-installation
|
||||||
* BungeeCord: `com.mysql.jdbc.Driver` for MySQL/MariaDB/PostgreSQL
|
* BungeeCord doesn't support SQLite per default, so you should change the configuration to MySQL or MariaDB
|
||||||
* Velocity: `fastlogin.mariadb.jdbc.Driver` for MySQL/MariaDB/PostgreSQL
|
|
||||||
* Note the embedded file storage SQLite is not available
|
|
||||||
* MySQL/MariaDB/PostgreSQL requires an external database server running. Check your server provider if there is one available
|
|
||||||
or install one.
|
|
||||||
6. Set proxy and Spigot in offline mode by setting the value `onlinemode` in your `config.yml` to false
|
|
||||||
7. You should *always* configure the firewall for your Spigot server so that it's only accessible through your proxy
|
|
||||||
* This is also the case without this plugin
|
|
||||||
* https://www.spigotmc.org/wiki/bungeecord-installation/#post-installation
|
|
||||||
|
218
bukkit/pom.xml
218
bukkit/pom.xml
@ -1,46 +1,15 @@
|
|||||||
<!--
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
|
||||||
SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<properties>
|
|
||||||
<nettyVersion>4.1.79.Final</nettyVersion>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.github.games647</groupId>
|
<groupId>com.github.games647</groupId>
|
||||||
<artifactId>fastlogin</artifactId>
|
<artifactId>fastlogin</artifactId>
|
||||||
<version>1.12-SNAPSHOT</version>
|
<version>1.11-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<!--This has to be in lowercase because it's used by plugin.yml-->
|
<!--This have to be in lowercase because it's used by plugin.yml-->
|
||||||
<artifactId>fastlogin.bukkit</artifactId>
|
<artifactId>fastlogin.bukkit</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
@ -49,16 +18,13 @@
|
|||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
<version>3.5.1</version>
|
<version>3.2.4</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||||
<shadedArtifactAttached>false</shadedArtifactAttached>
|
<shadedArtifactAttached>false</shadedArtifactAttached>
|
||||||
<relocations>
|
<relocations>
|
||||||
<relocation>
|
|
||||||
<pattern>org.yaml.snakeyaml</pattern>
|
|
||||||
<shadedPattern>fastlogin.yaml</shadedPattern>
|
|
||||||
</relocation>
|
|
||||||
<relocation>
|
<relocation>
|
||||||
<pattern>com.zaxxer.hikari</pattern>
|
<pattern>com.zaxxer.hikari</pattern>
|
||||||
<shadedPattern>fastlogin.hikari</shadedPattern>
|
<shadedPattern>fastlogin.hikari</shadedPattern>
|
||||||
@ -75,34 +41,11 @@
|
|||||||
<pattern>com.google.gson</pattern>
|
<pattern>com.google.gson</pattern>
|
||||||
<shadedPattern>fastlogin.gson</shadedPattern>
|
<shadedPattern>fastlogin.gson</shadedPattern>
|
||||||
</relocation>
|
</relocation>
|
||||||
<relocation>
|
|
||||||
<pattern>com.google.common</pattern>
|
|
||||||
<shadedPattern>fastlogin.guava</shadedPattern>
|
|
||||||
<excludes>
|
|
||||||
<exclude>com.google.common.collect.Multimap</exclude>
|
|
||||||
</excludes>
|
|
||||||
</relocation>
|
|
||||||
<relocation>
|
<relocation>
|
||||||
<pattern>io.papermc.lib</pattern>
|
<pattern>io.papermc.lib</pattern>
|
||||||
<shadedPattern>fastlogin.paperlib</shadedPattern>
|
<shadedPattern>fastlogin.paperlib</shadedPattern>
|
||||||
</relocation>
|
</relocation>
|
||||||
</relocations>
|
</relocations>
|
||||||
<!-- Rename the service file too to let SLF4J api find our own relocated jdk logger -->
|
|
||||||
<!-- Located in META-INF/services -->
|
|
||||||
<transformers>
|
|
||||||
<transformer
|
|
||||||
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
|
||||||
</transformers>
|
|
||||||
<minimizeJar>true</minimizeJar>
|
|
||||||
<filters>
|
|
||||||
<filter>
|
|
||||||
<artifact>*:*</artifact>
|
|
||||||
<excludes>
|
|
||||||
<exclude>META-INF/MANIFEST.MF</exclude>
|
|
||||||
<exclude>**/module-info.class</exclude>
|
|
||||||
</excludes>
|
|
||||||
</filter>
|
|
||||||
</filters>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
@ -126,7 +69,7 @@
|
|||||||
<!-- ProtocolLib -->
|
<!-- ProtocolLib -->
|
||||||
<repository>
|
<repository>
|
||||||
<id>dmulloy2-repo</id>
|
<id>dmulloy2-repo</id>
|
||||||
<url>https://repo.dmulloy2.net/repository/public/</url>
|
<url>https://repo.dmulloy2.net/nexus/repository/public/</url>
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>false</enabled>
|
<enabled>false</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
@ -136,6 +79,12 @@
|
|||||||
<repository>
|
<repository>
|
||||||
<id>codemc-releases</id>
|
<id>codemc-releases</id>
|
||||||
<url>https://repo.codemc.io/repository/maven-public/</url>
|
<url>https://repo.codemc.io/repository/maven-public/</url>
|
||||||
|
</repository>
|
||||||
|
|
||||||
|
<!-- GitHub automatic maven builds -->
|
||||||
|
<repository>
|
||||||
|
<id>jitpack.io</id>
|
||||||
|
<url>https://jitpack.io</url>
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>false</enabled>
|
<enabled>false</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
@ -149,15 +98,6 @@
|
|||||||
<enabled>false</enabled>
|
<enabled>false</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
</repository>
|
</repository>
|
||||||
|
|
||||||
<!-- GitHub automatic maven builds -->
|
|
||||||
<repository>
|
|
||||||
<id>jitpack.io</id>
|
|
||||||
<url>https://jitpack.io</url>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>false</enabled>
|
|
||||||
</snapshots>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -168,53 +108,28 @@
|
|||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- PaperSpigot API for correcting usercache usage -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.yaml</groupId>
|
<groupId>com.destroystokyo.paper</groupId>
|
||||||
<artifactId>snakeyaml</artifactId>
|
|
||||||
<version>1.33</version>
|
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- PaperSpigot API for correcting user cache usage -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.papermc.paper</groupId>
|
|
||||||
<artifactId>paper-api</artifactId>
|
<artifactId>paper-api</artifactId>
|
||||||
<version>1.19-R0.1-SNAPSHOT</version>
|
<version>1.15.2-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
<!-- Use our own newer api version -->
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-api</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- PaperLib for checking if server uses PaperSpigot -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mojang</groupId>
|
<groupId>io.papermc</groupId>
|
||||||
<artifactId>datafixerupper</artifactId>
|
<artifactId>paperlib</artifactId>
|
||||||
<version>5.0.28</version>
|
<version>1.0.6</version>
|
||||||
<scope>provided</scope>
|
<scope>compile</scope>
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!--Library for listening and sending Minecraft packets-->
|
<!--Library for listening and sending Minecraft packets-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.comphenix.protocol</groupId>
|
<groupId>com.comphenix.protocol</groupId>
|
||||||
<artifactId>ProtocolLib</artifactId>
|
<artifactId>ProtocolLib</artifactId>
|
||||||
<version>5.1.0</version>
|
<version>4.6.0</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!--Changing onlinemode on login process-->
|
<!--Changing onlinemode on login process-->
|
||||||
@ -222,63 +137,15 @@
|
|||||||
<groupId>com.github.ProtocolSupport</groupId>
|
<groupId>com.github.ProtocolSupport</groupId>
|
||||||
<artifactId>ProtocolSupport</artifactId>
|
<artifactId>ProtocolSupport</artifactId>
|
||||||
<!--4.29.dev after commit about API improvements-->
|
<!--4.29.dev after commit about API improvements-->
|
||||||
<version>master-66b494a8dd-1</version>
|
<version>3a80c661fe</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!--Floodgate for Xbox Live Authentication-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.geysermc.floodgate</groupId>
|
|
||||||
<artifactId>api</artifactId>
|
|
||||||
<version>${floodgate.version}</version>
|
|
||||||
<scope>provided</scope>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Bedrock player bridge -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.geysermc.geyser</groupId>
|
|
||||||
<artifactId>core</artifactId>
|
|
||||||
<version>${geyser.version}</version>
|
|
||||||
<scope>provided</scope>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- We need the API, but it was excluded above -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.geysermc.geyser</groupId>
|
|
||||||
<artifactId>api</artifactId>
|
|
||||||
<version>${geyser.version}</version>
|
|
||||||
<scope>provided</scope>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!--Provide premium placeholders-->
|
<!--Provide premium placeholders-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>me.clip</groupId>
|
<groupId>me.clip</groupId>
|
||||||
<artifactId>placeholderapi</artifactId>
|
<artifactId>placeholderapi</artifactId>
|
||||||
<version>2.11.5</version>
|
<version>2.10.8</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
@ -293,7 +160,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>fr.xephi</groupId>
|
<groupId>fr.xephi</groupId>
|
||||||
<artifactId>authme</artifactId>
|
<artifactId>authme</artifactId>
|
||||||
<version>5.6.0-beta2</version>
|
<version>5.4.0</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
@ -307,7 +174,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lenis0012.bukkit</groupId>
|
<groupId>com.lenis0012.bukkit</groupId>
|
||||||
<artifactId>loginsecurity</artifactId>
|
<artifactId>loginsecurity</artifactId>
|
||||||
<version>3.3.0</version>
|
<version>3.0.2</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
@ -376,32 +243,17 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>com.github.Mohist-Community.SodionAuth</groupId>
|
||||||
<artifactId>bcprov-jdk18on</artifactId>
|
<artifactId>SodionAuth-Bukkit</artifactId>
|
||||||
<version>1.77</version>
|
<version>b74392aa34</version>
|
||||||
<scope>test</scope>
|
<exclusions>
|
||||||
</dependency>
|
<exclusion>
|
||||||
|
<groupId>com.github.Mohist-Community.SodionAuth</groupId>
|
||||||
<dependency>
|
<artifactId>SodionAuth-Libs</artifactId>
|
||||||
<groupId>io.netty</groupId>
|
</exclusion>
|
||||||
<artifactId>netty-transport</artifactId>
|
</exclusions>
|
||||||
<version>${nettyVersion}</version>
|
<optional>true</optional>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.netty</groupId>
|
|
||||||
<artifactId>netty-codec</artifactId>
|
|
||||||
<version>${nettyVersion}</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Provided by the spigot, required for testing ProtocolLib -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>commons-lang</groupId>
|
|
||||||
<artifactId>commons-lang</artifactId>
|
|
||||||
<version>2.6</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -1,124 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit;
|
|
||||||
|
|
||||||
import com.github.games647.craftapi.model.skin.SkinProperty;
|
|
||||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
|
||||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a client connecting to the server.
|
|
||||||
* <p>
|
|
||||||
* This session is invalid if the player disconnects or the login was successful
|
|
||||||
*/
|
|
||||||
public class BukkitLoginSession extends LoginSession {
|
|
||||||
|
|
||||||
private static final byte[] EMPTY_ARRAY = {};
|
|
||||||
|
|
||||||
private final byte[] verifyToken;
|
|
||||||
|
|
||||||
private final ClientPublicKey clientPublicKey;
|
|
||||||
|
|
||||||
private boolean verified;
|
|
||||||
|
|
||||||
private SkinProperty skinProperty;
|
|
||||||
|
|
||||||
public BukkitLoginSession(String username, byte[] verifyToken, ClientPublicKey publicKey, boolean registered,
|
|
||||||
StoredProfile profile) {
|
|
||||||
super(username, registered, profile);
|
|
||||||
|
|
||||||
this.clientPublicKey = publicKey;
|
|
||||||
this.verifyToken = verifyToken.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
//available for BungeeCord
|
|
||||||
public BukkitLoginSession(String username, boolean registered) {
|
|
||||||
this(username, EMPTY_ARRAY, null, registered, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
//cracked player
|
|
||||||
public BukkitLoginSession(String username, StoredProfile profile) {
|
|
||||||
this(username, EMPTY_ARRAY, null, false, profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
//ProtocolSupport
|
|
||||||
public BukkitLoginSession(String username, boolean registered, StoredProfile profile) {
|
|
||||||
this(username, EMPTY_ARRAY, null, registered, profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the verify-token the server sent to the client.
|
|
||||||
* <p>
|
|
||||||
* Empty if it's a BungeeCord connection
|
|
||||||
*
|
|
||||||
* @return verify token from the server
|
|
||||||
*/
|
|
||||||
public synchronized byte[] getVerifyToken() {
|
|
||||||
return verifyToken.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public ClientPublicKey getClientPublicKey() {
|
|
||||||
return clientPublicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return premium skin if available
|
|
||||||
*/
|
|
||||||
public synchronized Optional<SkinProperty> getSkin() {
|
|
||||||
return Optional.ofNullable(skinProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the premium skin property which was retrieved by the session server
|
|
||||||
* @param skinProperty premium skin
|
|
||||||
*/
|
|
||||||
public synchronized void setSkinProperty(SkinProperty skinProperty) {
|
|
||||||
this.skinProperty = skinProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether the player has a premium (paid account) account and valid session
|
|
||||||
*
|
|
||||||
* @param verified whether the player has valid session
|
|
||||||
*/
|
|
||||||
public synchronized void setVerified(boolean verified) {
|
|
||||||
this.verified = verified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get whether the player has a premium (paid account) account and valid session
|
|
||||||
*
|
|
||||||
* @return whether the player has a valid session
|
|
||||||
*/
|
|
||||||
public synchronized boolean isVerified() {
|
|
||||||
return verified;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +1,24 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit;
|
package com.github.games647.fastlogin.bukkit;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.core.AsyncScheduler;
|
import com.github.games647.fastlogin.core.AsyncScheduler;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
public class BukkitScheduler extends AsyncScheduler {
|
public class BukkitScheduler extends AsyncScheduler {
|
||||||
|
|
||||||
|
private final Plugin plugin;
|
||||||
private final Executor syncExecutor;
|
private final Executor syncExecutor;
|
||||||
|
|
||||||
public BukkitScheduler(Plugin plugin, Logger logger) {
|
public BukkitScheduler(Plugin plugin, Logger logger, ThreadFactory threadFactory) {
|
||||||
super(logger, command -> Bukkit.getScheduler().runTaskAsynchronously(plugin, command));
|
super(logger, threadFactory);
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
syncExecutor = task -> Bukkit.getScheduler().runTask(plugin, task);
|
syncExecutor = r -> Bukkit.getScheduler().runTask(plugin, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Executor getSyncExecutor() {
|
public Executor getSyncExecutor() {
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.github.games647.fastlogin.bukkit;
|
||||||
|
|
||||||
|
import com.github.games647.fastlogin.bukkit.auth.BukkitLoginSession;
|
||||||
|
import com.github.games647.fastlogin.core.SessionManager;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
|
public class BukkitSessionManager extends SessionManager<PlayerQuitEvent, InetSocketAddress, BukkitLoginSession>
|
||||||
|
implements Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
@Override
|
||||||
|
public void onPlayQuit(PlayerQuitEvent quitEvent) {
|
||||||
|
Player player = quitEvent.getPlayer();
|
||||||
|
UUID playerId = player.getUniqueId();
|
||||||
|
endPlaySession(playerId);
|
||||||
|
}
|
||||||
|
}
|
@ -1,236 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit;
|
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.listener.BungeeListener;
|
|
||||||
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
|
||||||
import com.github.games647.fastlogin.core.message.LoginActionMessage;
|
|
||||||
import com.github.games647.fastlogin.core.message.NamespaceKey;
|
|
||||||
import com.google.common.io.ByteArrayDataOutput;
|
|
||||||
import com.google.common.io.ByteStreams;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Server;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.plugin.messaging.PluginMessageRecipient;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static com.github.games647.fastlogin.core.message.ChangePremiumMessage.CHANGE_CHANNEL;
|
|
||||||
import static com.github.games647.fastlogin.core.message.SuccessMessage.SUCCESS_CHANNEL;
|
|
||||||
import static java.util.stream.Collectors.toSet;
|
|
||||||
|
|
||||||
public class BungeeManager {
|
|
||||||
|
|
||||||
private static final String LEGACY_FILE_NAME = "proxy-whitelist.txt";
|
|
||||||
private static final String FILE_NAME = "allowed-proxies.txt";
|
|
||||||
|
|
||||||
//null if proxies allowed list is empty so bungeecord support is disabled
|
|
||||||
private Set<UUID> proxyIds;
|
|
||||||
|
|
||||||
private final FastLoginBukkit plugin;
|
|
||||||
private boolean enabled;
|
|
||||||
|
|
||||||
private final Collection<UUID> firedJoinEvents = new HashSet<>();
|
|
||||||
|
|
||||||
public BungeeManager(FastLoginBukkit plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cleanup() {
|
|
||||||
//remove old blocked status
|
|
||||||
Bukkit.getOnlinePlayers().forEach(player -> player.removeMetadata(plugin.getName(), plugin));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendPluginMessage(PluginMessageRecipient player, ChannelMessage message) {
|
|
||||||
if (player != null) {
|
|
||||||
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
|
|
||||||
message.writeTo(dataOutput);
|
|
||||||
|
|
||||||
NamespaceKey channel = new NamespaceKey(plugin.getName(), message.getChannelName());
|
|
||||||
player.sendPluginMessage(plugin, channel.getCombinedName(), dataOutput.toByteArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initialize() {
|
|
||||||
enabled = detectProxy();
|
|
||||||
|
|
||||||
if (enabled) {
|
|
||||||
proxyIds = loadBungeeCordIds();
|
|
||||||
if (proxyIds.isEmpty()) {
|
|
||||||
plugin.getLog().info("No valid IDs found. Minecraft proxy support cannot work in the current state");
|
|
||||||
}
|
|
||||||
|
|
||||||
registerPluginChannels();
|
|
||||||
plugin.getLog().info("Found enabled proxy configuration");
|
|
||||||
plugin.getLog().info("Remember to follow the proxy guide to complete your setup");
|
|
||||||
} else {
|
|
||||||
plugin.getLog().warn("Disabling Minecraft proxy configuration. Assuming direct connections from now on.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isProxySupported(String className, String fieldName)
|
|
||||||
throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
|
|
||||||
return Class.forName(className).getDeclaredField(fieldName).getBoolean(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isVelocityEnabled()
|
|
||||||
throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException, ClassNotFoundException,
|
|
||||||
NoSuchMethodException, InvocationTargetException {
|
|
||||||
try {
|
|
||||||
Class<?> globalConfig = Class.forName("io.papermc.paper.configuration.GlobalConfiguration");
|
|
||||||
Object global = globalConfig.getDeclaredMethod("get").invoke(null);
|
|
||||||
Object proxiesConfiguration = global.getClass().getDeclaredField("proxies").get(global);
|
|
||||||
|
|
||||||
Field velocitySectionField = proxiesConfiguration.getClass().getDeclaredField("velocity");
|
|
||||||
Object velocityConfig = velocitySectionField.get(proxiesConfiguration);
|
|
||||||
|
|
||||||
return velocityConfig.getClass().getDeclaredField("enabled").getBoolean(velocityConfig);
|
|
||||||
} catch (ClassNotFoundException classNotFoundException) {
|
|
||||||
// try again using the older Paper configuration, because the old class file still exists in newer versions
|
|
||||||
if (isProxySupported("com.destroystokyo.paper.PaperConfig", "velocitySupport")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean detectProxy() {
|
|
||||||
try {
|
|
||||||
if (isProxySupported("org.spigotmc.SpigotConfig", "bungee")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (ClassNotFoundException classNotFoundException) {
|
|
||||||
// leave stacktrace for class not found out
|
|
||||||
plugin.getLog().warn("Cannot check for BungeeCord support: {}", classNotFoundException.getMessage());
|
|
||||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
|
||||||
plugin.getLog().warn("Cannot check for BungeeCord support", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return isVelocityEnabled();
|
|
||||||
} catch (ClassNotFoundException classNotFoundException) {
|
|
||||||
plugin.getLog().warn("Cannot check for Velocity support in Paper: {}", classNotFoundException.getMessage());
|
|
||||||
} catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
|
|
||||||
plugin.getLog().warn("Cannot check for Velocity support in Paper", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerPluginChannels() {
|
|
||||||
Server server = Bukkit.getServer();
|
|
||||||
|
|
||||||
// check for incoming messages from the bungeecord version of this plugin
|
|
||||||
String groupId = plugin.getName();
|
|
||||||
String forceChannel = NamespaceKey.getCombined(groupId, LoginActionMessage.FORCE_CHANNEL);
|
|
||||||
server.getMessenger().registerIncomingPluginChannel(plugin, forceChannel, new BungeeListener(plugin));
|
|
||||||
|
|
||||||
// outgoing
|
|
||||||
String successChannel = new NamespaceKey(groupId, SUCCESS_CHANNEL).getCombinedName();
|
|
||||||
String changeChannel = new NamespaceKey(groupId, CHANGE_CHANNEL).getCombinedName();
|
|
||||||
server.getMessenger().registerOutgoingPluginChannel(plugin, successChannel);
|
|
||||||
server.getMessenger().registerOutgoingPluginChannel(plugin, changeChannel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<UUID> loadBungeeCordIds() {
|
|
||||||
Path proxiesFile = plugin.getPluginFolder().resolve(FILE_NAME);
|
|
||||||
Path legacyFile = plugin.getPluginFolder().resolve(LEGACY_FILE_NAME);
|
|
||||||
try {
|
|
||||||
if (Files.notExists(proxiesFile)) {
|
|
||||||
if (Files.exists(legacyFile)) {
|
|
||||||
Files.move(legacyFile, proxiesFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Files.notExists(legacyFile)) {
|
|
||||||
Files.createFile(proxiesFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Files.deleteIfExists(legacyFile);
|
|
||||||
try (Stream<String> lines = Files.lines(proxiesFile)) {
|
|
||||||
return lines.map(String::trim).map(UUID::fromString).collect(toSet());
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
plugin.getLog().error("Failed to read proxies", ex);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
plugin.getLog().error("Failed to retrieve proxy Id. Disabling BungeeCord support", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.emptySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isProxyAllowed(UUID proxyId) {
|
|
||||||
return proxyIds != null && proxyIds.contains(proxyId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark the event to be fired including the task delay.
|
|
||||||
*
|
|
||||||
* @param player joining player
|
|
||||||
*/
|
|
||||||
public synchronized void markJoinEventFired(Player player) {
|
|
||||||
firedJoinEvents.add(player.getUniqueId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the event fired including with the task delay. This necessary to restore the order of processing the
|
|
||||||
* BungeeCord messages after the PlayerJoinEvent fires including the delay.
|
|
||||||
* <p>
|
|
||||||
* If the join event fired, the delay exceeded, but it ran earlier and couldn't find the recently started login
|
|
||||||
* session. If not fired, we can start a new force login task. This will still match the requirement that we wait
|
|
||||||
* a certain time after the player join event fired.
|
|
||||||
*
|
|
||||||
* @param player joining player
|
|
||||||
* @return event fired including delay
|
|
||||||
*/
|
|
||||||
public synchronized boolean didJoinEventFired(Player player) {
|
|
||||||
return firedJoinEvents.contains(player.getUniqueId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Player quit clean up
|
|
||||||
*
|
|
||||||
* @param player joining player
|
|
||||||
*/
|
|
||||||
public synchronized void cleanup(Player player) {
|
|
||||||
firedJoinEvents.remove(player.getUniqueId());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,129 +1,81 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit;
|
package com.github.games647.fastlogin.bukkit;
|
||||||
|
|
||||||
import com.comphenix.protocol.ProtocolLibrary;
|
import com.github.games647.fastlogin.bukkit.auth.proxy.ProxyManager;
|
||||||
|
import com.github.games647.fastlogin.bukkit.auth.protocollib.ProtocolLibListener;
|
||||||
|
import com.github.games647.fastlogin.bukkit.auth.protocolsupport.ProtocolSupportListener;
|
||||||
import com.github.games647.fastlogin.bukkit.command.CrackedCommand;
|
import com.github.games647.fastlogin.bukkit.command.CrackedCommand;
|
||||||
import com.github.games647.fastlogin.bukkit.command.PremiumCommand;
|
import com.github.games647.fastlogin.bukkit.command.PremiumCommand;
|
||||||
|
import com.github.games647.fastlogin.bukkit.hook.DelayedAuthHook;
|
||||||
import com.github.games647.fastlogin.bukkit.listener.ConnectionListener;
|
import com.github.games647.fastlogin.bukkit.listener.ConnectionListener;
|
||||||
import com.github.games647.fastlogin.bukkit.listener.PaperCacheListener;
|
import com.github.games647.fastlogin.bukkit.listener.PaperPreLoginListener;
|
||||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.ProtocolLibListener;
|
|
||||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.SkinApplyListener;
|
|
||||||
import com.github.games647.fastlogin.bukkit.listener.protocolsupport.ProtocolSupportListener;
|
|
||||||
import com.github.games647.fastlogin.bukkit.task.DelayedAuthHook;
|
|
||||||
import com.github.games647.fastlogin.core.CommonUtil;
|
import com.github.games647.fastlogin.core.CommonUtil;
|
||||||
import com.github.games647.fastlogin.core.PremiumStatus;
|
import com.github.games647.fastlogin.core.PremiumStatus;
|
||||||
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
|
||||||
import com.github.games647.fastlogin.core.hooks.bedrock.GeyserService;
|
|
||||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||||
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.plugin.PluginManager;
|
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
|
||||||
import org.geysermc.floodgate.api.FloodgateApi;
|
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import io.papermc.lib.PaperLib;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This plugin checks if a player has a paid account and if so tries to skip offline mode authentication.
|
* This plugin checks if a player has a paid account and if so tries to skip offline mode authentication.
|
||||||
*/
|
*/
|
||||||
public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<CommandSender> {
|
public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<CommandSender> {
|
||||||
|
|
||||||
//1 minutes should be enough as a timeout for bad internet connection (Server, Client and Mojang)
|
private final BukkitSessionManager sessionManager = new BukkitSessionManager();
|
||||||
private final ConcurrentMap<String, BukkitLoginSession> loginSession = CommonUtil.buildCache(1, -1);
|
|
||||||
private final Map<UUID, PremiumStatus> premiumPlayers = new ConcurrentHashMap<>();
|
private final Map<UUID, PremiumStatus> premiumPlayers = new ConcurrentHashMap<>();
|
||||||
private final Logger logger;
|
private final FastLoginCore<Player, CommandSender, FastLoginBukkit> core = new FastLoginCore<>(this);
|
||||||
|
|
||||||
private boolean serverStarted;
|
private final Logger logger;
|
||||||
private BungeeManager bungeeManager;
|
|
||||||
private final BukkitScheduler scheduler;
|
private final BukkitScheduler scheduler;
|
||||||
private FastLoginCore<Player, CommandSender, FastLoginBukkit> core;
|
|
||||||
private FloodgateService floodgateService;
|
private ProxyManager proxyManager;
|
||||||
private GeyserService geyserService;
|
|
||||||
|
|
||||||
private PremiumPlaceholder premiumPlaceholder;
|
private PremiumPlaceholder premiumPlaceholder;
|
||||||
|
|
||||||
public FastLoginBukkit() {
|
public FastLoginBukkit() {
|
||||||
this.logger = CommonUtil.initializeLoggerService(getLogger());
|
this.logger = CommonUtil.createLoggerFromJDK(getLogger());
|
||||||
this.scheduler = new BukkitScheduler(this, logger);
|
this.scheduler = new BukkitScheduler(this, logger, getThreadFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
core = new FastLoginCore<>(this);
|
|
||||||
core.load();
|
core.load();
|
||||||
|
|
||||||
if (getServer().getOnlineMode()) {
|
if (getServer().getOnlineMode()) {
|
||||||
//we need to require offline to prevent a loginSession request for an offline player
|
//we need to require offline to prevent a loginSession request for a offline player
|
||||||
logger.error("Server has to be in offline mode");
|
logger.error("Server has to be in offline mode");
|
||||||
setEnabled(false);
|
setEnabled(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!initializeFloodgate()) {
|
proxyManager = new ProxyManager(this);
|
||||||
setEnabled(false);
|
proxyManager.initialize();
|
||||||
}
|
|
||||||
|
|
||||||
bungeeManager = new BungeeManager(this);
|
|
||||||
bungeeManager.initialize();
|
|
||||||
|
|
||||||
PluginManager pluginManager = getServer().getPluginManager();
|
PluginManager pluginManager = getServer().getPluginManager();
|
||||||
if (bungeeManager.isEnabled()) {
|
if (!proxyManager.isEnabled()) {
|
||||||
markInitialized();
|
|
||||||
} else {
|
|
||||||
if (!core.setupDatabase()) {
|
if (!core.setupDatabase()) {
|
||||||
setEnabled(false);
|
setEnabled(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pluginManager.isPluginEnabled("ProtocolSupport")) {
|
if (pluginManager.isPluginEnabled("ProtocolSupport")) {
|
||||||
pluginManager.registerEvents(new ProtocolSupportListener(this, core.getAntiBot()), this);
|
pluginManager.registerEvents(new ProtocolSupportListener(this, core.getRateLimiter()), this);
|
||||||
} else if (pluginManager.isPluginEnabled("ProtocolLib")) {
|
} else if (pluginManager.isPluginEnabled("ProtocolLib")) {
|
||||||
ProtocolLibListener.register(this, core.getAntiBot(), core.getConfig().getBoolean("verifyClientKeys"));
|
ProtocolLibListener.register(this, core.getRateLimiter());
|
||||||
|
|
||||||
//if server is using paper - we need to set the skin at pre login anyway, so no need for this listener
|
|
||||||
if (!isPaper() && getConfig().getBoolean("forwardSkin")) {
|
|
||||||
pluginManager.registerEvents(new SkinApplyListener(this), this);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Either ProtocolLib or ProtocolSupport have to be installed if you don't use BungeeCord");
|
logger.warn("Either ProtocolLib or ProtocolSupport have to be installed if you don't use proxies");
|
||||||
setEnabled(false);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,14 +84,14 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
|||||||
|
|
||||||
pluginManager.registerEvents(new ConnectionListener(this), this);
|
pluginManager.registerEvents(new ConnectionListener(this), this);
|
||||||
|
|
||||||
//if server is using paper - we need to add one more listener to correct the user cache usage
|
//if server is using paper - we need to add one more listener to correct the usercache usage
|
||||||
if (isPaper()) {
|
if (PaperLib.isPaper()) {
|
||||||
pluginManager.registerEvents(new PaperCacheListener(this), this);
|
pluginManager.registerEvents(new PaperPreLoginListener(this), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
//register commands using a unique name
|
//register commands using a unique name
|
||||||
Optional.ofNullable(getCommand("premium")).ifPresent(c -> c.setExecutor(new PremiumCommand(this)));
|
getCommand("premium").setExecutor(new PremiumCommand(this));
|
||||||
Optional.ofNullable(getCommand("cracked")).ifPresent(c -> c.setExecutor(new CrackedCommand(this)));
|
getCommand("cracked").setExecutor(new CrackedCommand(this));
|
||||||
|
|
||||||
if (pluginManager.isPluginEnabled("PlaceholderAPI")) {
|
if (pluginManager.isPluginEnabled("PlaceholderAPI")) {
|
||||||
premiumPlaceholder = new PremiumPlaceholder(this);
|
premiumPlaceholder = new PremiumPlaceholder(this);
|
||||||
@ -147,45 +99,14 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean initializeFloodgate() {
|
|
||||||
if (getServer().getPluginManager().getPlugin("Geyser-Spigot") != null) {
|
|
||||||
geyserService = new GeyserService(GeyserImpl.getInstance(), core);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getServer().getPluginManager().getPlugin("floodgate") != null) {
|
|
||||||
floodgateService = new FloodgateService(FloodgateApi.getInstance(), core);
|
|
||||||
|
|
||||||
// Check Floodgate config values and return
|
|
||||||
return floodgateService.isValidFloodgateConfigString("autoLoginFloodgate")
|
|
||||||
&& floodgateService.isValidFloodgateConfigString("allowFloodgateNameConflict");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
loginSession.clear();
|
|
||||||
premiumPlayers.clear();
|
premiumPlayers.clear();
|
||||||
|
core.close();
|
||||||
|
|
||||||
if (core != null) {
|
proxyManager.cleanup();
|
||||||
core.close();
|
if (getServer().getPluginManager().isPluginEnabled("PlaceholderAPI") && premiumPlaceholder != null) {
|
||||||
}
|
premiumPlaceholder.unregister();
|
||||||
|
|
||||||
if (bungeeManager != null) {
|
|
||||||
bungeeManager.cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (premiumPlaceholder != null && getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) {
|
|
||||||
try {
|
|
||||||
premiumPlaceholder.unregister();
|
|
||||||
} catch (Exception | NoSuchMethodError exception) {
|
|
||||||
logger.error("Failed to unregister placeholder", exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getServer().getPluginManager().isPluginEnabled("ProtocolLib")) {
|
|
||||||
ProtocolLibrary.getProtocolManager().getAsynchronousManager().unregisterAsyncHandlers(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,82 +114,40 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
|||||||
return core;
|
return core;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the premium status of an online player.
|
||||||
|
*
|
||||||
|
* @param onlinePlayer
|
||||||
|
* @return the online status or unknown if an error happened, the player isn't online or a proxy doesn't send
|
||||||
|
* us the status message yet (This means you cannot check the login status on the PlayerJoinEvent).
|
||||||
|
* @deprecated this method could be removed in future versions and exists only as a temporarily solution
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public PremiumStatus getStatus(UUID onlinePlayer) {
|
||||||
|
StoredProfile playSession = sessionManager.getPlaySession(onlinePlayer);
|
||||||
|
return Optional.ofNullable(playSession).map(profile -> {
|
||||||
|
if (profile.isPremium())
|
||||||
|
return PremiumStatus.PREMIUM;
|
||||||
|
return PremiumStatus.CRACKED;
|
||||||
|
}).orElse(PremiumStatus.UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a thread-safe map about players which are connecting to the server are being checked to be premium (paid
|
* Gets a thread-safe map about players which are connecting to the server are being checked to be premium (paid
|
||||||
* account)
|
* account)
|
||||||
*
|
*
|
||||||
* @return a thread-safe loginSession map
|
* @return a thread-safe loginSession map
|
||||||
*/
|
*/
|
||||||
public ConcurrentMap<String, BukkitLoginSession> getLoginSessions() {
|
public BukkitSessionManager getSessionManager() {
|
||||||
return loginSession;
|
return sessionManager;
|
||||||
}
|
|
||||||
|
|
||||||
public BukkitLoginSession getSession(InetSocketAddress address) {
|
|
||||||
String id = getSessionId(address);
|
|
||||||
return loginSession.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSessionId(InetSocketAddress address) {
|
|
||||||
return address.getAddress().getHostAddress() + ':' + address.getPort();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void putSession(InetSocketAddress address, BukkitLoginSession session) {
|
|
||||||
String id = getSessionId(address);
|
|
||||||
loginSession.put(id, session);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeSession(InetSocketAddress address) {
|
|
||||||
String id = getSessionId(address);
|
|
||||||
loginSession.remove(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<UUID, PremiumStatus> getPremiumPlayers() {
|
public Map<UUID, PremiumStatus> getPremiumPlayers() {
|
||||||
return premiumPlayers;
|
return premiumPlayers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public ProxyManager getProxyManager() {
|
||||||
* Fetches the premium status of an online player.
|
return proxyManager;
|
||||||
* {@snippet :
|
|
||||||
* // Bukkit's players object after successful authentication i.e. PlayerJoinEvent
|
|
||||||
* // except for proxies like BungeeCord and Velocity where the details are sent delayed (1-2 seconds)
|
|
||||||
* Player player;
|
|
||||||
* PremiumStatus status = JavaPlugin.getPlugin(FastLoginBukkit.class).getStatus(player.getUniqueId());
|
|
||||||
* switch (status) {
|
|
||||||
* case CRACKED:
|
|
||||||
* // player is offline
|
|
||||||
* break;
|
|
||||||
* case PREMIUM:
|
|
||||||
* // account is premium and player passed the verification
|
|
||||||
* break;
|
|
||||||
* case UNKNOWN:
|
|
||||||
* // no record about this player
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @param onlinePlayer player that is currently online player (play state)
|
|
||||||
* @return the online status or unknown if an error happened, the player isn't online or BungeeCord doesn't send
|
|
||||||
* us the status message yet (This means you cannot check the login status on the PlayerJoinEvent).
|
|
||||||
*/
|
|
||||||
public @NotNull PremiumStatus getStatus(@NotNull UUID onlinePlayer) {
|
|
||||||
return premiumPlayers.getOrDefault(onlinePlayer, PremiumStatus.UNKNOWN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait before the server is fully started. This is workaround, because connections right on startup are not
|
|
||||||
* injected by ProtocolLib
|
|
||||||
*
|
|
||||||
* @return true if ProtocolLib can now intercept packets
|
|
||||||
*/
|
|
||||||
public boolean isServerFullyStarted() {
|
|
||||||
return serverStarted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void markInitialized() {
|
|
||||||
this.serverStarted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BungeeManager getBungeeManager() {
|
|
||||||
return bungeeManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -290,45 +169,4 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
|||||||
public void sendMessage(CommandSender receiver, String message) {
|
public void sendMessage(CommandSender receiver, String message) {
|
||||||
receiver.sendMessage(message);
|
receiver.sendMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a plugin is installed on the server
|
|
||||||
*
|
|
||||||
* @param name the name of the plugin
|
|
||||||
* @return true if the plugin is installed
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean isPluginInstalled(String name) {
|
|
||||||
// the plugin may be enabled after FastLogin, so isPluginEnabled() won't work here
|
|
||||||
return Bukkit.getServer().getPluginManager().getPlugin(name) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FloodgateService getFloodgateService() {
|
|
||||||
return floodgateService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GeyserService getGeyserService() {
|
|
||||||
return geyserService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BedrockService<?> getBedrockService() {
|
|
||||||
if (floodgateService != null) {
|
|
||||||
return floodgateService;
|
|
||||||
}
|
|
||||||
return geyserService;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isPaper() {
|
|
||||||
return isClassAvailable("com.destroystokyo.paper.PaperConfig").isPresent()
|
|
||||||
|| isClassAvailable("io.papermc.paper.configuration.Configuration").isPresent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<Class<?>> isClassAvailable(String clazzName) {
|
|
||||||
try {
|
|
||||||
return Optional.of(Class.forName(clazzName));
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,22 @@
|
|||||||
/*
|
package com.github.games647.fastlogin.bukkit;
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.task;
|
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
import com.github.games647.fastlogin.bukkit.auth.BukkitLoginSession;
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
|
||||||
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginAutoLoginEvent;
|
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginAutoLoginEvent;
|
||||||
import com.github.games647.fastlogin.core.PremiumStatus;
|
import com.github.games647.fastlogin.core.PremiumStatus;
|
||||||
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
import com.github.games647.fastlogin.core.message.SuccessMessage;
|
import com.github.games647.fastlogin.core.message.SuccessMessage;
|
||||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||||
import com.github.games647.fastlogin.core.shared.ForceLoginManagement;
|
import com.github.games647.fastlogin.core.auth.ForceLoginManagement;
|
||||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
import com.github.games647.fastlogin.core.auth.LoginSession;
|
||||||
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
|
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.metadata.FixedMetadataValue;
|
import org.bukkit.metadata.FixedMetadataValue;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> {
|
public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> {
|
||||||
|
|
||||||
public ForceLoginTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player,
|
public ForceLoginTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player,
|
||||||
@ -51,7 +26,7 @@ public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// block this target player for BungeeCord ID brute force attacks
|
// block this target player for proxy ID brute force attacks
|
||||||
FastLoginBukkit plugin = core.getPlugin();
|
FastLoginBukkit plugin = core.getPlugin();
|
||||||
player.setMetadata(core.getPlugin().getName(), new FixedMetadataValue(plugin, true));
|
player.setMetadata(core.getPlugin().getName(), new FixedMetadataValue(plugin, true));
|
||||||
|
|
||||||
@ -80,8 +55,8 @@ public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onForceActionSuccess(LoginSession session) {
|
public void onForceActionSuccess(LoginSession session) {
|
||||||
if (core.getPlugin().getBungeeManager().isEnabled()) {
|
if (core.getPlugin().getProxyManager().isEnabled()) {
|
||||||
core.getPlugin().getBungeeManager().sendPluginMessage(player, new SuccessMessage());
|
core.getPlugin().getProxyManager().sendPluginMessage(player, new SuccessMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit;
|
|
||||||
|
|
||||||
import java.net.InetAddress;
|
|
||||||
|
|
||||||
public final class InetUtils {
|
|
||||||
|
|
||||||
private InetUtils() {
|
|
||||||
// Utility
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies if the given IP address is from the local network
|
|
||||||
*
|
|
||||||
* @param address IP address
|
|
||||||
* @return true if address is from local network or even from the device itself (loopback)
|
|
||||||
*/
|
|
||||||
public static boolean isLocalAddress(InetAddress address) {
|
|
||||||
// Loopback addresses like 127.0.* (IPv4) or [::1] (IPv6)
|
|
||||||
return address.isLoopbackAddress()
|
|
||||||
// Example: 10.0.0.0, 172.16.0.0, 192.168.0.0, fec0::/10 (deprecated)
|
|
||||||
// Ref: https://en.wikipedia.org/wiki/IP_address#Private_addresses
|
|
||||||
|| address.isSiteLocalAddress()
|
|
||||||
// Example: 169.254.0.0/16, fe80::/10
|
|
||||||
// Ref: https://en.wikipedia.org/wiki/IP_address#Address_autoconfiguration
|
|
||||||
|| address.isLinkLocalAddress()
|
|
||||||
// non deprecated unique site-local that java doesn't check yet -> fc00::/7
|
|
||||||
|| isIPv6UniqueSiteLocal(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isIPv6UniqueSiteLocal(InetAddress address) {
|
|
||||||
// ref: https://en.wikipedia.org/wiki/Unique_local_address
|
|
||||||
|
|
||||||
// currently undefined but could be used in the near future fc00::/8
|
|
||||||
return (address.getAddress()[0] & 0xFF) == 0xFC
|
|
||||||
// in use for unique site-local fd00::/8
|
|
||||||
|| (address.getAddress()[0] & 0xFF) == 0xFD;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +1,8 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit;
|
package com.github.games647.fastlogin.bukkit;
|
||||||
|
|
||||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||||
import org.bukkit.OfflinePlayer;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import org.bukkit.OfflinePlayer;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class PremiumPlaceholder extends PlaceholderExpansion {
|
public class PremiumPlaceholder extends PlaceholderExpansion {
|
||||||
|
|
||||||
@ -43,17 +15,7 @@ public class PremiumPlaceholder extends PlaceholderExpansion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean persist() {
|
public String onRequest(OfflinePlayer player, String identifier) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull List<String> getPlaceholders() {
|
|
||||||
return Collections.singletonList(PLACEHOLDER_VARIABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String onRequest(OfflinePlayer player, @NotNull String identifier) {
|
|
||||||
// player is null if offline
|
// player is null if offline
|
||||||
if (player != null && PLACEHOLDER_VARIABLE.equals(identifier)) {
|
if (player != null && PLACEHOLDER_VARIABLE.equals(identifier)) {
|
||||||
return plugin.getStatus(player.getUniqueId()).getReadableName();
|
return plugin.getStatus(player.getUniqueId()).getReadableName();
|
||||||
@ -63,7 +25,7 @@ public class PremiumPlaceholder extends PlaceholderExpansion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull String getIdentifier() {
|
public String getIdentifier() {
|
||||||
return plugin.getName();
|
return plugin.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,12 +35,12 @@ public class PremiumPlaceholder extends PlaceholderExpansion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull String getAuthor() {
|
public String getAuthor() {
|
||||||
return String.join(", ", plugin.getDescription().getAuthors());
|
return String.join(", ", plugin.getDescription().getAuthors());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull String getVersion() {
|
public String getVersion() {
|
||||||
return plugin.getDescription().getVersion();
|
return plugin.getDescription().getVersion();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
package com.github.games647.fastlogin.bukkit.auth;
|
||||||
|
|
||||||
|
import com.github.games647.craftapi.model.skin.SkinProperty;
|
||||||
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
|
import com.github.games647.fastlogin.core.auth.LoginSession;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a client connecting to the server.
|
||||||
|
*
|
||||||
|
* This session is invalid if the player disconnects or the login was successful
|
||||||
|
*/
|
||||||
|
public class BukkitLoginSession extends LoginSession {
|
||||||
|
|
||||||
|
private static final byte[] EMPTY_ARRAY = {};
|
||||||
|
|
||||||
|
private final String serverId;
|
||||||
|
private final byte[] verifyToken;
|
||||||
|
|
||||||
|
private boolean verified;
|
||||||
|
|
||||||
|
private SkinProperty skinProperty;
|
||||||
|
|
||||||
|
public BukkitLoginSession(String username, String serverId, byte[] verifyToken, boolean registered
|
||||||
|
, StoredProfile profile) {
|
||||||
|
super(username, registered, profile);
|
||||||
|
|
||||||
|
this.serverId = serverId;
|
||||||
|
this.verifyToken = verifyToken.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
// available for proxies
|
||||||
|
public BukkitLoginSession(String username, boolean registered) {
|
||||||
|
this(username, "", EMPTY_ARRAY, registered, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
//cracked player
|
||||||
|
public BukkitLoginSession(String username, StoredProfile profile) {
|
||||||
|
this(username, "", EMPTY_ARRAY, false, profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
//ProtocolSupport
|
||||||
|
public BukkitLoginSession(String username, boolean registered, StoredProfile profile) {
|
||||||
|
this(username, "", EMPTY_ARRAY, registered, profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the verify token the server sent to the client.
|
||||||
|
*
|
||||||
|
* Empty if it's a proxy connection
|
||||||
|
*
|
||||||
|
* @return the verify token from the server
|
||||||
|
*/
|
||||||
|
public synchronized byte[] getVerifyToken() {
|
||||||
|
return verifyToken.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return premium skin if available
|
||||||
|
*/
|
||||||
|
public synchronized Optional<SkinProperty> getSkin() {
|
||||||
|
return Optional.ofNullable(skinProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the premium skin property which was retrieved by the session server
|
||||||
|
* @param skinProperty premium skin
|
||||||
|
*/
|
||||||
|
public synchronized void setSkinProperty(SkinProperty skinProperty) {
|
||||||
|
this.skinProperty = skinProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the player has a premium (paid account) account and valid session
|
||||||
|
*
|
||||||
|
* @param verified whether the player has valid session
|
||||||
|
*/
|
||||||
|
public synchronized void setVerified(boolean verified) {
|
||||||
|
this.verified = verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether the player has a premium (paid account) account and valid session
|
||||||
|
*
|
||||||
|
* @return whether the player has a valid session
|
||||||
|
*/
|
||||||
|
public synchronized boolean isVerified() {
|
||||||
|
return verified;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,131 @@
|
|||||||
|
package com.github.games647.fastlogin.bukkit.auth.protocollib;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.util.Random;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encryption and decryption minecraft util for connection between servers
|
||||||
|
* and paid Minecraft account clients.
|
||||||
|
*
|
||||||
|
* @see net.minecraft.server.MinecraftEncryption
|
||||||
|
*/
|
||||||
|
public class EncryptionUtil {
|
||||||
|
|
||||||
|
public static final int VERIFY_TOKEN_LENGTH = 4;
|
||||||
|
public static final String KEY_PAIR_ALGORITHM = "RSA";
|
||||||
|
|
||||||
|
private EncryptionUtil() {
|
||||||
|
// utility
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a RSA key pair
|
||||||
|
*
|
||||||
|
* @return The RSA key pair.
|
||||||
|
*/
|
||||||
|
public static KeyPair generateKeyPair() {
|
||||||
|
// KeyPair b()
|
||||||
|
try {
|
||||||
|
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_PAIR_ALGORITHM);
|
||||||
|
|
||||||
|
keyPairGenerator.initialize(1_024);
|
||||||
|
return keyPairGenerator.generateKeyPair();
|
||||||
|
} catch (NoSuchAlgorithmException nosuchalgorithmexception) {
|
||||||
|
// Should be existing in every vm
|
||||||
|
throw new ExceptionInInitializerError(nosuchalgorithmexception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random token. This is used to verify that we are communicating with the same player
|
||||||
|
* in a login session.
|
||||||
|
*
|
||||||
|
* @param random random generator
|
||||||
|
* @return an error with 4 bytes long
|
||||||
|
*/
|
||||||
|
public static byte[] generateVerifyToken(Random random) {
|
||||||
|
// extracted from LoginListener
|
||||||
|
byte[] token = new byte[VERIFY_TOKEN_LENGTH];
|
||||||
|
random.nextBytes(token);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the server id based on client and server data.
|
||||||
|
*
|
||||||
|
* @param sessionId session for the current login attempt
|
||||||
|
* @param sharedSecret shared secret between the client and the server
|
||||||
|
* @param publicKey public key of the server
|
||||||
|
* @return the server id formatted as a hexadecimal string.
|
||||||
|
*/
|
||||||
|
public static String getServerIdHashString(String sessionId, SecretKey sharedSecret, PublicKey publicKey) {
|
||||||
|
// found in LoginListener
|
||||||
|
try {
|
||||||
|
byte[] serverHash = getServerIdHash(sessionId, publicKey, sharedSecret);
|
||||||
|
return (new BigInteger(serverHash)).toString(16);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts the content and extracts the key spec.
|
||||||
|
*
|
||||||
|
* @param privateKey private server key
|
||||||
|
* @param sharedKey the encrypted shared key
|
||||||
|
* @return shared secret key
|
||||||
|
* @throws GeneralSecurityException if it fails to decrypt the data
|
||||||
|
*/
|
||||||
|
public static SecretKey decryptSharedKey(PrivateKey privateKey, byte[] sharedKey) throws GeneralSecurityException {
|
||||||
|
// SecretKey a(PrivateKey var0, byte[] var1)
|
||||||
|
return new SecretKeySpec(decrypt(privateKey, sharedKey), "AES");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decrypt(PrivateKey key, byte[] data) throws GeneralSecurityException {
|
||||||
|
// b(Key var0, byte[] var1)
|
||||||
|
Cipher cipher = Cipher.getInstance(key.getAlgorithm());
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, key);
|
||||||
|
return decrypt(cipher, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypted the given data using the cipher.
|
||||||
|
*
|
||||||
|
* @param cipher decryption cypher initialized with the private key
|
||||||
|
* @param data the encrypted data
|
||||||
|
* @return clear text data
|
||||||
|
* @throws GeneralSecurityException if it fails to decrypt the data
|
||||||
|
*/
|
||||||
|
private static byte[] decrypt(Cipher cipher, byte[] data) throws GeneralSecurityException {
|
||||||
|
// inlined: byte[] a(int var0, Key var1, byte[] var2), Cipher a(int var0, String var1, Key
|
||||||
|
// var2)
|
||||||
|
return cipher.doFinal(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] getServerIdHash(
|
||||||
|
String sessionId, PublicKey publicKey, SecretKey sharedSecret)
|
||||||
|
throws NoSuchAlgorithmException {
|
||||||
|
// byte[] a(String var0, PublicKey var1, SecretKey var2)
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||||
|
|
||||||
|
// inlined from byte[] a(String var0, byte[]... var1)
|
||||||
|
digest.update(sessionId.getBytes(StandardCharsets.ISO_8859_1));
|
||||||
|
digest.update(sharedSecret.getEncoded());
|
||||||
|
digest.update(publicKey.getEncoded());
|
||||||
|
|
||||||
|
return digest.digest();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.github.games647.fastlogin.bukkit.auth.protocollib;
|
||||||
|
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerLoginEvent;
|
||||||
|
import org.bukkit.event.player.PlayerLoginEvent.Result;
|
||||||
|
|
||||||
|
public class InitializedListener implements Listener {
|
||||||
|
|
||||||
|
private final ProtocolLibListener module;
|
||||||
|
|
||||||
|
protected InitializedListener(ProtocolLibListener protocolLibModule) {
|
||||||
|
this.module = protocolLibModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
|
public void onPlayerLogin(PlayerLoginEvent loginEvent) {
|
||||||
|
if (loginEvent.getResult() == Result.ALLOWED && !module.isReadyToInject()) {
|
||||||
|
loginEvent.disallow(Result.KICK_OTHER, module.getPlugin().getCore().getMessage("not-started"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,67 +1,39 @@
|
|||||||
/*
|
package com.github.games647.fastlogin.bukkit.auth.protocollib;
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
|
||||||
|
|
||||||
import com.comphenix.protocol.ProtocolLibrary;
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
import com.github.games647.fastlogin.bukkit.auth.BukkitLoginSession;
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent;
|
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent;
|
||||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
|
||||||
import com.github.games647.fastlogin.core.shared.JoinManagement;
|
|
||||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
import org.bukkit.command.CommandSender;
|
import com.github.games647.fastlogin.core.auth.JoinManagement;
|
||||||
import org.bukkit.entity.Player;
|
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
||||||
|
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
public class NameCheckTask extends JoinManagement<Player, CommandSender, ProtocolLibLoginSource>
|
public class NameCheckTask extends JoinManagement<Player, CommandSender, ProtocolLibLoginSource>
|
||||||
implements Runnable {
|
implements Runnable {
|
||||||
|
|
||||||
private final FastLoginBukkit plugin;
|
private final FastLoginBukkit plugin;
|
||||||
private final PacketEvent packetEvent;
|
private final PacketEvent packetEvent;
|
||||||
|
private final PublicKey publicKey;
|
||||||
private final ClientPublicKey clientKey;
|
|
||||||
private final PublicKey serverKey;
|
|
||||||
|
|
||||||
private final Random random;
|
private final Random random;
|
||||||
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private final String username;
|
private final String username;
|
||||||
|
|
||||||
public NameCheckTask(FastLoginBukkit plugin, Random random, Player player, PacketEvent packetEvent,
|
protected NameCheckTask(FastLoginBukkit plugin, PacketEvent packetEvent, Random random,
|
||||||
String username, ClientPublicKey clientKey, PublicKey serverKey) {
|
Player player, String username, PublicKey publicKey) {
|
||||||
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
|
super(plugin.getCore(), plugin.getCore().getAuthPluginHook());
|
||||||
|
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.packetEvent = packetEvent;
|
this.packetEvent = packetEvent;
|
||||||
this.clientKey = clientKey;
|
this.publicKey = publicKey;
|
||||||
this.serverKey = serverKey;
|
|
||||||
this.random = random;
|
this.random = random;
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
@ -70,15 +42,14 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
super.onLogin(username, new ProtocolLibLoginSource(player, random, serverKey, clientKey));
|
super.onLogin(username, new ProtocolLibLoginSource(packetEvent, player, random, publicKey));
|
||||||
} finally {
|
} finally {
|
||||||
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
|
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FastLoginPreLoginEvent callFastLoginPreLoginEvent(String username, ProtocolLibLoginSource source,
|
public FastLoginPreLoginEvent callFastLoginPreLoginEvent(String username, ProtocolLibLoginSource source, StoredProfile profile) {
|
||||||
StoredProfile profile) {
|
|
||||||
BukkitFastLoginPreLoginEvent event = new BukkitFastLoginPreLoginEvent(username, source, profile);
|
BukkitFastLoginPreLoginEvent event = new BukkitFastLoginPreLoginEvent(username, source, profile);
|
||||||
plugin.getServer().getPluginManager().callEvent(event);
|
plugin.getServer().getPluginManager().callEvent(event);
|
||||||
return event;
|
return event;
|
||||||
@ -87,10 +58,10 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
|
|||||||
//Minecraft server implementation
|
//Minecraft server implementation
|
||||||
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161
|
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161
|
||||||
@Override
|
@Override
|
||||||
public void requestPremiumLogin(ProtocolLibLoginSource source, StoredProfile profile,
|
public void requestPremiumLogin(ProtocolLibLoginSource source, StoredProfile profile
|
||||||
String username, boolean registered) {
|
, String username, boolean registered) {
|
||||||
try {
|
try {
|
||||||
source.enableOnlinemode();
|
source.setOnlineMode();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
plugin.getLog().error("Cannot send encryption packet. Falling back to cracked login for: {}", profile, ex);
|
plugin.getLog().error("Cannot send encryption packet. Falling back to cracked login for: {}", profile, ex);
|
||||||
return;
|
return;
|
||||||
@ -99,11 +70,11 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
|
|||||||
String ip = player.getAddress().getAddress().getHostAddress();
|
String ip = player.getAddress().getAddress().getHostAddress();
|
||||||
core.getPendingLogin().put(ip + username, new Object());
|
core.getPendingLogin().put(ip + username, new Object());
|
||||||
|
|
||||||
|
String serverId = source.getServerId();
|
||||||
byte[] verify = source.getVerifyToken();
|
byte[] verify = source.getVerifyToken();
|
||||||
ClientPublicKey clientKey = source.getClientKey();
|
|
||||||
|
|
||||||
BukkitLoginSession playerSession = new BukkitLoginSession(username, verify, clientKey, registered, profile);
|
BukkitLoginSession playerSession = new BukkitLoginSession(username, serverId, verify, registered, profile);
|
||||||
plugin.putSession(player.getAddress(), playerSession);
|
plugin.getSessionManager().startLoginSession(player.getAddress(), playerSession);
|
||||||
//cancel only if the player has a paid account otherwise login as normal offline player
|
//cancel only if the player has a paid account otherwise login as normal offline player
|
||||||
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
|
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
|
||||||
packetEvent.setCancelled(true);
|
packetEvent.setCancelled(true);
|
||||||
@ -113,6 +84,6 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
|
|||||||
@Override
|
@Override
|
||||||
public void startCrackedSession(ProtocolLibLoginSource source, StoredProfile profile, String username) {
|
public void startCrackedSession(ProtocolLibLoginSource source, StoredProfile profile, String username) {
|
||||||
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
|
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
|
||||||
plugin.putSession(player.getAddress(), loginSession);
|
plugin.getSessionManager().startLoginSession(player.getAddress(), loginSession);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,124 @@
|
|||||||
|
package com.github.games647.fastlogin.bukkit.auth.protocollib;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
|
import com.comphenix.protocol.events.PacketAdapter;
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
|
import com.github.games647.fastlogin.core.auth.RateLimiter;
|
||||||
|
|
||||||
|
import io.papermc.lib.PaperLib;
|
||||||
|
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
|
|
||||||
|
import static com.comphenix.protocol.PacketType.Login.Client.ENCRYPTION_BEGIN;
|
||||||
|
import static com.comphenix.protocol.PacketType.Login.Client.START;
|
||||||
|
|
||||||
|
public class ProtocolLibListener extends PacketAdapter {
|
||||||
|
|
||||||
|
private final FastLoginBukkit plugin;
|
||||||
|
|
||||||
|
//just create a new once on plugin enable. This used for verify token generation
|
||||||
|
private final SecureRandom random = new SecureRandom();
|
||||||
|
private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
|
||||||
|
private final RateLimiter rateLimiter;
|
||||||
|
|
||||||
|
// Wait before the server is fully started. This is workaround, because connections right on startup are not
|
||||||
|
// injected by ProtocolLib
|
||||||
|
private boolean serverStarted;
|
||||||
|
|
||||||
|
protected ProtocolLibListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
|
||||||
|
//run async in order to not block the server, because we are making api calls to Mojang
|
||||||
|
super(params()
|
||||||
|
.plugin(plugin)
|
||||||
|
.types(START, ENCRYPTION_BEGIN)
|
||||||
|
.optionAsync());
|
||||||
|
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.rateLimiter = rateLimiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void register(FastLoginBukkit plugin, RateLimiter rateLimiter) {
|
||||||
|
//they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError
|
||||||
|
ProtocolLibListener packetListener = new ProtocolLibListener(plugin, rateLimiter);
|
||||||
|
ProtocolLibrary.getProtocolManager()
|
||||||
|
.getAsynchronousManager()
|
||||||
|
.registerAsyncHandler(packetListener)
|
||||||
|
.start();
|
||||||
|
|
||||||
|
PluginManager pluginManager = Bukkit.getServer().getPluginManager();
|
||||||
|
pluginManager.registerEvents(new InitializedListener(packetListener), plugin);
|
||||||
|
|
||||||
|
//if server is using paper - we need to set the skin at pre login anyway, so no need for this listener
|
||||||
|
if (!PaperLib.isPaper() && plugin.getConfig().getBoolean("forwardSkin")) {
|
||||||
|
pluginManager.registerEvents(new SkinApplyListener(plugin), plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacketReceiving(PacketEvent packetEvent) {
|
||||||
|
if (packetEvent.isCancelled() || plugin.getCore().getAuthPluginHook() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
markReadyToInject();
|
||||||
|
|
||||||
|
Player sender = packetEvent.getPlayer();
|
||||||
|
PacketType packetType = packetEvent.getPacketType();
|
||||||
|
if (packetType == START) {
|
||||||
|
if (!rateLimiter.tryAcquire()) {
|
||||||
|
plugin.getLog().warn("Join limit hit - Ignoring player {}", sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onLogin(packetEvent, sender);
|
||||||
|
} else {
|
||||||
|
onEncryptionBegin(packetEvent, sender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onEncryptionBegin(PacketEvent packetEvent, Player sender) {
|
||||||
|
byte[] sharedSecret = packetEvent.getPacket().getByteArrays().read(0);
|
||||||
|
|
||||||
|
packetEvent.getAsyncMarker().incrementProcessingDelay();
|
||||||
|
Runnable verifyTask = new VerifyResponseTask(plugin, packetEvent, sender, sharedSecret, keyPair);
|
||||||
|
plugin.getScheduler().runAsync(verifyTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onLogin(PacketEvent packetEvent, Player player) {
|
||||||
|
//this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
|
||||||
|
String sessionKey = player.getAddress().toString();
|
||||||
|
|
||||||
|
//remove old data every time on a new login in order to keep the session only for one person
|
||||||
|
plugin.getSessionManager().endLoginSession(player.getAddress());
|
||||||
|
|
||||||
|
//player.getName() won't work at this state
|
||||||
|
PacketContainer packet = packetEvent.getPacket();
|
||||||
|
|
||||||
|
String username = packet.getGameProfiles().read(0).getName();
|
||||||
|
plugin.getLog().trace("GameProfile {} with {} connecting", sessionKey, username);
|
||||||
|
|
||||||
|
packetEvent.getAsyncMarker().incrementProcessingDelay();
|
||||||
|
Runnable nameCheckTask = new NameCheckTask(plugin, packetEvent, random, player, username, keyPair.getPublic());
|
||||||
|
plugin.getScheduler().runAsync(nameCheckTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markReadyToInject() {
|
||||||
|
this.serverStarted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReadyToInject() {
|
||||||
|
return serverStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FastLoginBukkit getPlugin() {
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
}
|
@ -1,68 +1,44 @@
|
|||||||
/*
|
package com.github.games647.fastlogin.bukkit.auth.protocollib;
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
|
||||||
|
|
||||||
import com.comphenix.protocol.ProtocolLibrary;
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
import com.comphenix.protocol.ProtocolManager;
|
import com.comphenix.protocol.ProtocolManager;
|
||||||
import com.comphenix.protocol.events.PacketContainer;
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
import com.comphenix.protocol.reflect.StructureModifier;
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
import com.github.games647.fastlogin.core.auth.LoginSource;
|
||||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT;
|
import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT;
|
||||||
import static com.comphenix.protocol.PacketType.Login.Server.ENCRYPTION_BEGIN;
|
import static com.comphenix.protocol.PacketType.Login.Server.ENCRYPTION_BEGIN;
|
||||||
|
|
||||||
class ProtocolLibLoginSource implements LoginSource {
|
class ProtocolLibLoginSource implements LoginSource {
|
||||||
|
|
||||||
|
private final PacketEvent packetEvent;
|
||||||
private final Player player;
|
private final Player player;
|
||||||
|
|
||||||
private final Random random;
|
private final Random random;
|
||||||
|
|
||||||
private final ClientPublicKey clientKey;
|
|
||||||
private final PublicKey publicKey;
|
private final PublicKey publicKey;
|
||||||
|
|
||||||
private final String serverId = "";
|
private final String serverId = "";
|
||||||
private byte[] verifyToken;
|
private byte[] verifyToken;
|
||||||
|
|
||||||
ProtocolLibLoginSource(Player player, Random random, PublicKey serverPublicKey, ClientPublicKey clientKey) {
|
protected ProtocolLibLoginSource(PacketEvent packetEvent, Player player, Random random, PublicKey publicKey) {
|
||||||
|
this.packetEvent = packetEvent;
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.random = random;
|
this.random = random;
|
||||||
this.publicKey = serverPublicKey;
|
this.publicKey = publicKey;
|
||||||
this.clientKey = clientKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableOnlinemode() {
|
public void setOnlineMode() throws Exception {
|
||||||
verifyToken = EncryptionUtil.generateVerifyToken(random);
|
verifyToken = EncryptionUtil.generateVerifyToken(random);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -85,12 +61,12 @@ class ProtocolLibLoginSource implements LoginSource {
|
|||||||
|
|
||||||
newPacket.getByteArrays().write(verifyField, verifyToken);
|
newPacket.getByteArrays().write(verifyField, verifyToken);
|
||||||
|
|
||||||
//serverId is an empty string
|
//serverId is a empty string
|
||||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, newPacket);
|
ProtocolLibrary.getProtocolManager().sendServerPacket(player, newPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void kick(String message) {
|
public void kick(String message) throws InvocationTargetException {
|
||||||
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
|
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
|
||||||
|
|
||||||
PacketContainer kickPacket = new PacketContainer(DISCONNECT);
|
PacketContainer kickPacket = new PacketContainer(DISCONNECT);
|
||||||
@ -108,11 +84,7 @@ class ProtocolLibLoginSource implements LoginSource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InetSocketAddress getAddress() {
|
public InetSocketAddress getAddress() {
|
||||||
return player.getAddress();
|
return packetEvent.getPlayer().getAddress();
|
||||||
}
|
|
||||||
|
|
||||||
public ClientPublicKey getClientKey() {
|
|
||||||
return clientKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getServerId() {
|
public String getServerId() {
|
||||||
@ -125,11 +97,12 @@ class ProtocolLibLoginSource implements LoginSource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return this.getClass().getSimpleName() + '{'
|
return this.getClass().getSimpleName() + '{' +
|
||||||
+ "player=" + player
|
"packetEvent=" + packetEvent +
|
||||||
+ ", random=" + random
|
", player=" + player +
|
||||||
+ ", serverId='" + serverId + '\''
|
", random=" + random +
|
||||||
+ ", verifyToken=" + Arrays.toString(verifyToken)
|
", serverId='" + serverId + '\'' +
|
||||||
+ '}';
|
", verifyToken=" + Arrays.toString(verifyToken) +
|
||||||
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package com.github.games647.fastlogin.bukkit.auth.protocollib;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||||
|
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||||
|
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
|
||||||
|
import com.github.games647.craftapi.model.skin.Textures;
|
||||||
|
import com.github.games647.fastlogin.bukkit.auth.BukkitLoginSession;
|
||||||
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerLoginEvent;
|
||||||
|
import org.bukkit.event.player.PlayerLoginEvent.Result;
|
||||||
|
|
||||||
|
public class SkinApplyListener implements Listener {
|
||||||
|
|
||||||
|
private static final Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
|
||||||
|
private static final MethodAccessor GET_PROPERTIES = Accessors.getMethodAccessor(GAME_PROFILE, "getProperties");
|
||||||
|
|
||||||
|
private final FastLoginBukkit plugin;
|
||||||
|
|
||||||
|
protected SkinApplyListener(FastLoginBukkit plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.LOW)
|
||||||
|
//run this on the loginEvent to let skins plugins see the skin like in normal Minecraft behaviour
|
||||||
|
public void onPlayerLogin(PlayerLoginEvent loginEvent) {
|
||||||
|
if (loginEvent.getResult() != Result.ALLOWED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player player = loginEvent.getPlayer();
|
||||||
|
|
||||||
|
//go through every session, because player.getAddress is null
|
||||||
|
//loginEvent.getAddress is just a InetAddress not InetSocketAddress, so not unique enough
|
||||||
|
BukkitLoginSession session = plugin.getSessionManager().getLoginSession(player.getAddress());
|
||||||
|
if (session.getUsername().equals(player.getName())) {
|
||||||
|
session.getSkin().ifPresent(skin -> applySkin(player, skin.getValue(), skin.getSignature()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySkin(Player player, String skinData, String signature) {
|
||||||
|
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
|
||||||
|
WrappedSignedProperty skin = WrappedSignedProperty.fromValues(Textures.KEY, skinData, signature);
|
||||||
|
gameProfile.getProperties().put(Textures.KEY, skin);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,264 @@
|
|||||||
|
package com.github.games647.fastlogin.bukkit.auth.protocollib;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||||
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||||
|
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||||
|
import com.github.games647.craftapi.model.auth.Verification;
|
||||||
|
import com.github.games647.craftapi.model.skin.SkinProperty;
|
||||||
|
import com.github.games647.craftapi.resolver.MojangResolver;
|
||||||
|
import com.github.games647.fastlogin.bukkit.auth.BukkitLoginSession;
|
||||||
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import static com.comphenix.protocol.PacketType.Login.Client.START;
|
||||||
|
import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT;
|
||||||
|
|
||||||
|
public class VerifyResponseTask implements Runnable {
|
||||||
|
|
||||||
|
private final FastLoginBukkit plugin;
|
||||||
|
private final PacketEvent packetEvent;
|
||||||
|
private final KeyPair serverKey;
|
||||||
|
|
||||||
|
private final Player player;
|
||||||
|
|
||||||
|
private final byte[] sharedSecret;
|
||||||
|
|
||||||
|
private static Method encryptMethod;
|
||||||
|
private static Method cipherMethod;
|
||||||
|
|
||||||
|
protected VerifyResponseTask(FastLoginBukkit plugin, PacketEvent packetEvent, Player player,
|
||||||
|
byte[] sharedSecret, KeyPair keyPair) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.packetEvent = packetEvent;
|
||||||
|
this.player = player;
|
||||||
|
this.sharedSecret = Arrays.copyOf(sharedSecret, sharedSecret.length);
|
||||||
|
this.serverKey = keyPair;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
BukkitLoginSession session = plugin.getSessionManager().getLoginSession(player.getAddress());
|
||||||
|
if (session == null) {
|
||||||
|
disconnect("invalid-request", true
|
||||||
|
, "GameProfile {0} tried to send encryption response at invalid state", player.getAddress());
|
||||||
|
} else {
|
||||||
|
verifyResponse(session);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
//this is a fake packet; it shouldn't be send to the server
|
||||||
|
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
|
||||||
|
packetEvent.setCancelled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyResponse(BukkitLoginSession session) {
|
||||||
|
PrivateKey privateKey = serverKey.getPrivate();
|
||||||
|
|
||||||
|
SecretKey loginKey;
|
||||||
|
try {
|
||||||
|
loginKey = EncryptionUtil.decryptSharedKey(privateKey, sharedSecret);
|
||||||
|
} catch (GeneralSecurityException securityEx) {
|
||||||
|
disconnect("error-kick", false, "Cannot decrypt received contents", securityEx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!checkVerifyToken(session) || !enableEncryption(loginKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
disconnect("error-kick", false, "Cannot decrypt received contents", ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String serverId = EncryptionUtil.getServerIdHashString("", loginKey, serverKey.getPublic());
|
||||||
|
|
||||||
|
String requestedUsername = session.getRequestUsername();
|
||||||
|
InetSocketAddress socketAddress = player.getAddress();
|
||||||
|
try {
|
||||||
|
MojangResolver resolver = plugin.getCore().getResolver();
|
||||||
|
InetAddress address = socketAddress.getAddress();
|
||||||
|
Optional<Verification> response = resolver.hasJoined(requestedUsername, serverId, address);
|
||||||
|
if (response.isPresent()) {
|
||||||
|
Verification verification = response.get();
|
||||||
|
plugin.getLog().info("Profile {} has a verified premium account: {}", requestedUsername, verification);
|
||||||
|
String realUsername = verification.getName();
|
||||||
|
if (realUsername == null) {
|
||||||
|
disconnect("invalid-session", true, "Username field null for {}", requestedUsername);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkinProperty[] properties = verification.getProperties();
|
||||||
|
if (properties.length > 0) {
|
||||||
|
session.setSkinProperty(properties[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
session.setVerifiedUsername(realUsername);
|
||||||
|
session.setUuid(verification.getId());
|
||||||
|
session.setVerified(true);
|
||||||
|
|
||||||
|
setPremiumUUID(session.getUuid());
|
||||||
|
receiveFakeStartPacket(realUsername);
|
||||||
|
} else {
|
||||||
|
//user tried to fake a authentication
|
||||||
|
disconnect("invalid-session", true
|
||||||
|
, "GameProfile {0} ({1}) tried to log in with an invalid session ServerId: {2}"
|
||||||
|
, session.getRequestUsername(), socketAddress, serverId);
|
||||||
|
}
|
||||||
|
} catch (IOException ioEx) {
|
||||||
|
disconnect("error-kick", false, "Failed to connect to session server", ioEx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPremiumUUID(UUID premiumUUID) {
|
||||||
|
if (plugin.getConfig().getBoolean("premiumUuid") && premiumUUID != null) {
|
||||||
|
try {
|
||||||
|
Object networkManager = getNetworkManager();
|
||||||
|
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/NetworkManager.java#L69
|
||||||
|
FieldUtils.writeField(networkManager, "spoofedUUID", premiumUUID, true);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
plugin.getLog().error("Error setting premium uuid of {}", player, exc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkVerifyToken(BukkitLoginSession session) throws GeneralSecurityException {
|
||||||
|
byte[] requestVerify = session.getVerifyToken();
|
||||||
|
//encrypted verify token
|
||||||
|
byte[] responseVerify = packetEvent.getPacket().getByteArrays().read(1);
|
||||||
|
|
||||||
|
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L182
|
||||||
|
if (!Arrays.equals(requestVerify, EncryptionUtil.decrypt(serverKey.getPrivate(), responseVerify))) {
|
||||||
|
//check if the verify token are equal to the server sent one
|
||||||
|
disconnect("invalid-verify-token", true
|
||||||
|
, "GameProfile {0} ({1}) tried to login with an invalid verify token. Server: {2} Client: {3}"
|
||||||
|
, session.getRequestUsername(), packetEvent.getPlayer().getAddress(), requestVerify, responseVerify);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//try to get the networkManager from ProtocolLib
|
||||||
|
private Object getNetworkManager() throws IllegalAccessException, ClassNotFoundException {
|
||||||
|
Object injectorContainer = TemporaryPlayerFactory.getInjectorFromPlayer(player);
|
||||||
|
|
||||||
|
//ChannelInjector
|
||||||
|
Class<?> injectorClass = Class.forName("com.comphenix.protocol.injector.netty.Injector");
|
||||||
|
Object rawInjector = FuzzyReflection.getFieldValue(injectorContainer, injectorClass, true);
|
||||||
|
return FieldUtils.readField(rawInjector, "networkManager", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean enableEncryption(SecretKey loginKey) throws IllegalArgumentException {
|
||||||
|
// Initialize method reflections
|
||||||
|
if (encryptMethod == null) {
|
||||||
|
Class<?> networkManagerClass = MinecraftReflection.getNetworkManagerClass();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Try to get the old (pre MC 1.16.4) encryption method
|
||||||
|
encryptMethod = FuzzyReflection.fromClass(networkManagerClass)
|
||||||
|
.getMethodByParameters("a", SecretKey.class);
|
||||||
|
} catch (IllegalArgumentException exception) {
|
||||||
|
// Get the new encryption method
|
||||||
|
encryptMethod = FuzzyReflection.fromClass(networkManagerClass)
|
||||||
|
.getMethodByParameters("a", Cipher.class, Cipher.class);
|
||||||
|
|
||||||
|
// Get the needed Cipher helper method (used to generate ciphers from login key)
|
||||||
|
Class<?> encryptionClass = MinecraftReflection.getMinecraftClass("MinecraftEncryption");
|
||||||
|
cipherMethod = FuzzyReflection.fromClass(encryptionClass)
|
||||||
|
.getMethodByParameters("a", int.class, Key.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Object networkManager = this.getNetworkManager();
|
||||||
|
|
||||||
|
// If cipherMethod is null - use old encryption (pre MC 1.16.4), otherwise use the new cipher one
|
||||||
|
if (cipherMethod == null) {
|
||||||
|
// Encrypt/decrypt packet flow, this behaviour is expected by the client
|
||||||
|
encryptMethod.invoke(networkManager, loginKey);
|
||||||
|
} else {
|
||||||
|
// Create ciphers from login key
|
||||||
|
Object decryptionCipher = cipherMethod.invoke(null, Cipher.DECRYPT_MODE, loginKey);
|
||||||
|
Object encryptionCipher = cipherMethod.invoke(null, Cipher.ENCRYPT_MODE, loginKey);
|
||||||
|
|
||||||
|
// Encrypt/decrypt packet flow, this behaviour is expected by the client
|
||||||
|
encryptMethod.invoke(networkManager, decryptionCipher, encryptionCipher);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
disconnect("error-kick", false, "Couldn't enable encryption", ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disconnect(String reasonKey, boolean debug, String logMessage, Object... arguments) {
|
||||||
|
if (debug) {
|
||||||
|
plugin.getLog().debug(logMessage, arguments);
|
||||||
|
} else {
|
||||||
|
plugin.getLog().error(logMessage, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
kickPlayer(plugin.getCore().getMessage(reasonKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void kickPlayer(String reason) {
|
||||||
|
PacketContainer kickPacket = new PacketContainer(DISCONNECT);
|
||||||
|
kickPacket.getChatComponents().write(0, WrappedChatComponent.fromText(reason));
|
||||||
|
try {
|
||||||
|
//send kick packet at login state
|
||||||
|
//the normal event.getPlayer.kickPlayer(String) method does only work at play state
|
||||||
|
ProtocolLibrary.getProtocolManager().sendServerPacket(player, kickPacket);
|
||||||
|
//tell the server that we want to close the connection
|
||||||
|
player.kickPlayer("Disconnect");
|
||||||
|
} catch (InvocationTargetException ex) {
|
||||||
|
plugin.getLog().error("Error sending kick packet for: {}", player, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//fake a new login packet in order to let the server handle all the other stuff
|
||||||
|
private void receiveFakeStartPacket(String username) {
|
||||||
|
//see StartPacketListener for packet information
|
||||||
|
PacketContainer startPacket = new PacketContainer(START);
|
||||||
|
|
||||||
|
//uuid is ignored by the packet definition
|
||||||
|
WrappedGameProfile fakeProfile = new WrappedGameProfile(UUID.randomUUID(), username);
|
||||||
|
startPacket.getGameProfiles().write(0, fakeProfile);
|
||||||
|
try {
|
||||||
|
//we don't want to handle our own packets so ignore filters
|
||||||
|
ProtocolLibrary.getProtocolManager().recieveClientPacket(player, startPacket, false);
|
||||||
|
} catch (InvocationTargetException | IllegalAccessException ex) {
|
||||||
|
plugin.getLog().warn("Failed to fake a new start packet for: {}", username, ex);
|
||||||
|
//cancel the event in order to prevent the server receiving an invalid packet
|
||||||
|
kickPlayer(plugin.getCore().getMessage("error-kick"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.github.games647.fastlogin.bukkit.auth.protocolsupport;
|
||||||
|
|
||||||
|
import com.github.games647.fastlogin.core.auth.LoginSource;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
|
import protocolsupport.api.events.PlayerLoginStartEvent;
|
||||||
|
|
||||||
|
public class ProtocolLoginSource implements LoginSource {
|
||||||
|
|
||||||
|
private final PlayerLoginStartEvent loginStartEvent;
|
||||||
|
|
||||||
|
protected ProtocolLoginSource(PlayerLoginStartEvent loginStartEvent) {
|
||||||
|
this.loginStartEvent = loginStartEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOnlineMode() {
|
||||||
|
loginStartEvent.setOnlineMode(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void kick(String message) {
|
||||||
|
loginStartEvent.denyLogin(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InetSocketAddress getAddress() {
|
||||||
|
return loginStartEvent.getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerLoginStartEvent getLoginStartEvent() {
|
||||||
|
return loginStartEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.getClass().getSimpleName() + '{' +
|
||||||
|
"loginStartEvent=" + loginStartEvent +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,102 @@
|
|||||||
|
package com.github.games647.fastlogin.bukkit.auth.protocolsupport;
|
||||||
|
|
||||||
|
import com.github.games647.craftapi.UUIDAdapter;
|
||||||
|
import com.github.games647.fastlogin.bukkit.auth.BukkitLoginSession;
|
||||||
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
|
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent;
|
||||||
|
import com.github.games647.fastlogin.core.auth.RateLimiter;
|
||||||
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
|
import com.github.games647.fastlogin.core.auth.JoinManagement;
|
||||||
|
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import protocolsupport.api.events.ConnectionCloseEvent;
|
||||||
|
import protocolsupport.api.events.PlayerLoginStartEvent;
|
||||||
|
import protocolsupport.api.events.PlayerProfileCompleteEvent;
|
||||||
|
|
||||||
|
public class ProtocolSupportListener extends JoinManagement<Player, CommandSender, ProtocolLoginSource>
|
||||||
|
implements Listener {
|
||||||
|
|
||||||
|
private final FastLoginBukkit plugin;
|
||||||
|
private final RateLimiter rateLimiter;
|
||||||
|
|
||||||
|
public ProtocolSupportListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
|
||||||
|
super(plugin.getCore(), plugin.getCore().getAuthPluginHook());
|
||||||
|
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.rateLimiter = rateLimiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onLoginStart(PlayerLoginStartEvent loginStartEvent) {
|
||||||
|
if (loginStartEvent.isLoginDenied() || plugin.getCore().getAuthPluginHook() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rateLimiter.tryAcquire()) {
|
||||||
|
plugin.getLog().warn("Join limit hit - Ignoring player {}", loginStartEvent.getConnection());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String username = loginStartEvent.getConnection().getProfile().getName();
|
||||||
|
InetSocketAddress address = loginStartEvent.getAddress();
|
||||||
|
|
||||||
|
//remove old data every time on a new login in order to keep the session only for one person
|
||||||
|
plugin.getSessionManager().endLoginSession(address);
|
||||||
|
|
||||||
|
super.onLogin(username, new ProtocolLoginSource(loginStartEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onConnectionClosed(ConnectionCloseEvent closeEvent) {
|
||||||
|
InetSocketAddress address = closeEvent.getConnection().getAddress();
|
||||||
|
plugin.getSessionManager().endLoginSession(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPropertiesResolve(PlayerProfileCompleteEvent profileCompleteEvent) {
|
||||||
|
InetSocketAddress address = profileCompleteEvent.getAddress();
|
||||||
|
BukkitLoginSession session = plugin.getSessionManager().getLoginSession(address);
|
||||||
|
|
||||||
|
if (session != null && profileCompleteEvent.getConnection().getProfile().isOnlineMode()) {
|
||||||
|
session.setVerified(true);
|
||||||
|
|
||||||
|
if (!plugin.getConfig().getBoolean("premiumUuid")) {
|
||||||
|
String username = Optional.ofNullable(profileCompleteEvent.getForcedName())
|
||||||
|
.orElse(profileCompleteEvent.getConnection().getProfile().getName());
|
||||||
|
profileCompleteEvent.setForcedUUID(UUIDAdapter.generateOfflineId(username));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FastLoginPreLoginEvent callFastLoginPreLoginEvent(String username, ProtocolLoginSource source, StoredProfile profile) {
|
||||||
|
BukkitFastLoginPreLoginEvent event = new BukkitFastLoginPreLoginEvent(username, source, profile);
|
||||||
|
plugin.getServer().getPluginManager().callEvent(event);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestPremiumLogin(ProtocolLoginSource source, StoredProfile profile, String username
|
||||||
|
, boolean registered) {
|
||||||
|
source.setOnlineMode();
|
||||||
|
|
||||||
|
String ip = source.getAddress().getAddress().getHostAddress();
|
||||||
|
plugin.getCore().getPendingLogin().put(ip + username, new Object());
|
||||||
|
|
||||||
|
BukkitLoginSession playerSession = new BukkitLoginSession(username, registered, profile);
|
||||||
|
plugin.getSessionManager().startLoginSession(source.getAddress(), playerSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startCrackedSession(ProtocolLoginSource source, StoredProfile profile, String username) {
|
||||||
|
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
|
||||||
|
plugin.getSessionManager().startLoginSession(source.getAddress(), loginSession);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,169 @@
|
|||||||
|
package com.github.games647.fastlogin.bukkit.auth.proxy;
|
||||||
|
|
||||||
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
|
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
||||||
|
import com.github.games647.fastlogin.core.message.LoginActionMessage;
|
||||||
|
import com.github.games647.fastlogin.core.message.NamespaceKey;
|
||||||
|
import com.google.common.io.ByteArrayDataOutput;
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.messaging.PluginMessageRecipient;
|
||||||
|
|
||||||
|
import static com.github.games647.fastlogin.core.message.ChangePremiumMessage.CHANGE_CHANNEL;
|
||||||
|
import static com.github.games647.fastlogin.core.message.SuccessMessage.SUCCESS_CHANNEL;
|
||||||
|
import static java.util.stream.Collectors.toSet;
|
||||||
|
|
||||||
|
public class ProxyManager {
|
||||||
|
|
||||||
|
private static final String LEGACY_FILE_NAME = "proxy-whitelist.txt";
|
||||||
|
private static final String FILE_NAME = "allowed-proxies.txt";
|
||||||
|
|
||||||
|
//null if proxies allowed list is empty so proxy support is disabled
|
||||||
|
private Set<UUID> proxyIds;
|
||||||
|
|
||||||
|
private final FastLoginBukkit plugin;
|
||||||
|
private boolean enabled;
|
||||||
|
|
||||||
|
private final Set<UUID> firedJoinEvents = new HashSet<>();
|
||||||
|
|
||||||
|
public ProxyManager(FastLoginBukkit plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanup() {
|
||||||
|
//remove old blocked status
|
||||||
|
Bukkit.getOnlinePlayers().forEach(player -> player.removeMetadata(plugin.getName(), plugin));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPluginMessage(PluginMessageRecipient player, ChannelMessage message) {
|
||||||
|
if (player != null) {
|
||||||
|
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
|
||||||
|
message.writeTo(dataOutput);
|
||||||
|
|
||||||
|
NamespaceKey channel = new NamespaceKey(plugin.getName(), message.getChannelName());
|
||||||
|
player.sendPluginMessage(plugin, channel.getCombinedName(), dataOutput.toByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize() {
|
||||||
|
try {
|
||||||
|
enabled = detectProxy();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
plugin.getLog().warn("Cannot check proxy support. Fallback to non-proxy mode", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
proxyIds = loadProxyIds();
|
||||||
|
registerPluginChannels();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean detectProxy() throws Exception {
|
||||||
|
try {
|
||||||
|
enabled = Class.forName("org.spigotmc.SpigotConfig").getDeclaredField("bungee").getBoolean(null);
|
||||||
|
return enabled;
|
||||||
|
} catch (ClassNotFoundException notFoundEx) {
|
||||||
|
//ignore server has no proxy support
|
||||||
|
return false;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerPluginChannels() {
|
||||||
|
Server server = Bukkit.getServer();
|
||||||
|
|
||||||
|
// check for incoming messages from the proxy version of this plugin
|
||||||
|
String groupId = plugin.getName();
|
||||||
|
String forceChannel = NamespaceKey.getCombined(groupId, LoginActionMessage.FORCE_CHANNEL);
|
||||||
|
server.getMessenger().registerIncomingPluginChannel(plugin, forceChannel, new ProxyMessagingListener(plugin));
|
||||||
|
|
||||||
|
// outgoing
|
||||||
|
String successChannel = new NamespaceKey(groupId, SUCCESS_CHANNEL).getCombinedName();
|
||||||
|
String changeChannel = new NamespaceKey(groupId, CHANGE_CHANNEL).getCombinedName();
|
||||||
|
server.getMessenger().registerOutgoingPluginChannel(plugin, successChannel);
|
||||||
|
server.getMessenger().registerOutgoingPluginChannel(plugin, changeChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<UUID> loadProxyIds() {
|
||||||
|
Path proxiesFile = plugin.getPluginFolder().resolve(FILE_NAME);
|
||||||
|
Path legacyFile = plugin.getPluginFolder().resolve(LEGACY_FILE_NAME);
|
||||||
|
try {
|
||||||
|
if (Files.notExists(proxiesFile)) {
|
||||||
|
if (Files.exists(legacyFile)) {
|
||||||
|
Files.move(legacyFile, proxiesFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Files.notExists(legacyFile)) {
|
||||||
|
Files.createFile(proxiesFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Files.deleteIfExists(legacyFile);
|
||||||
|
try (Stream<String> lines = Files.lines(proxiesFile)) {
|
||||||
|
return lines.map(String::trim)
|
||||||
|
.map(UUID::fromString)
|
||||||
|
.collect(toSet());
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
plugin.getLog().error("Failed to read proxies", ex);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
plugin.getLog().error("Failed to retrieve proxy Id. Disabling proxy support", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isProxyAllowed(UUID proxyId) {
|
||||||
|
return proxyIds != null && proxyIds.contains(proxyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark the event to be fired including the task delay.
|
||||||
|
*
|
||||||
|
* @param player
|
||||||
|
*/
|
||||||
|
public synchronized void markJoinEventFired(Player player) {
|
||||||
|
firedJoinEvents.add(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the event fired including with the task delay. This necessary to restore the order of processing the
|
||||||
|
* proxy messages after the PlayerJoinEvent fires including the delay.
|
||||||
|
*
|
||||||
|
* If the join event fired, the delay exceeded, but it ran earlier and couldn't find the recently started login
|
||||||
|
* session. If not fired, we can start a new force login task. This will still match the requirement that we wait
|
||||||
|
* a certain time after the player join event fired.
|
||||||
|
*
|
||||||
|
* @param player
|
||||||
|
* @return event fired including delay
|
||||||
|
*/
|
||||||
|
public synchronized boolean didJoinEventFired(Player player) {
|
||||||
|
return firedJoinEvents.contains(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Player quit clean up
|
||||||
|
*
|
||||||
|
* @param player
|
||||||
|
*/
|
||||||
|
public synchronized void cleanup(Player player) {
|
||||||
|
firedJoinEvents.remove(player.getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
@ -1,62 +1,38 @@
|
|||||||
/*
|
package com.github.games647.fastlogin.bukkit.auth.proxy;
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener;
|
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
import com.github.games647.fastlogin.bukkit.auth.BukkitLoginSession;
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.bukkit.task.ForceLoginTask;
|
import com.github.games647.fastlogin.bukkit.ForceLoginTask;
|
||||||
import com.github.games647.fastlogin.core.PremiumStatus;
|
import com.github.games647.fastlogin.core.PremiumStatus;
|
||||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||||
import com.github.games647.fastlogin.core.message.LoginActionMessage;
|
import com.github.games647.fastlogin.core.message.LoginActionMessage;
|
||||||
import com.github.games647.fastlogin.core.message.LoginActionMessage.Type;
|
import com.github.games647.fastlogin.core.message.LoginActionMessage.Type;
|
||||||
import com.google.common.io.ByteArrayDataInput;
|
import com.google.common.io.ByteArrayDataInput;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible for receiving messages from a BungeeCord instance.
|
* Responsible for receiving messages from a proxy instance.
|
||||||
* <p>
|
*
|
||||||
* This class also receives the plugin message from the bungeecord version of this plugin in order to get notified if
|
* This class also receives the plugin message from the proxy version of this plugin in order to get notified if
|
||||||
* the connection is in online mode.
|
* the connection is in online mode.
|
||||||
*/
|
*/
|
||||||
public class BungeeListener implements PluginMessageListener {
|
public class ProxyMessagingListener implements PluginMessageListener {
|
||||||
|
|
||||||
private final FastLoginBukkit plugin;
|
private final FastLoginBukkit plugin;
|
||||||
|
|
||||||
public BungeeListener(FastLoginBukkit plugin) {
|
protected ProxyMessagingListener(FastLoginBukkit plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPluginMessageReceived(@NotNull String channel, Player player, byte[] message) {
|
public void onPluginMessageReceived(String channel, Player player, byte[] message) {
|
||||||
ByteArrayDataInput dataInput = ByteStreams.newDataInput(message);
|
ByteArrayDataInput dataInput = ByteStreams.newDataInput(message);
|
||||||
|
|
||||||
LoginActionMessage loginMessage = new LoginActionMessage();
|
LoginActionMessage loginMessage = new LoginActionMessage();
|
||||||
@ -66,7 +42,7 @@ public class BungeeListener implements PluginMessageListener {
|
|||||||
|
|
||||||
Player targetPlayer = player;
|
Player targetPlayer = player;
|
||||||
if (!loginMessage.getPlayerName().equals(player.getName())) {
|
if (!loginMessage.getPlayerName().equals(player.getName())) {
|
||||||
targetPlayer = Bukkit.getPlayerExact(loginMessage.getPlayerName());
|
targetPlayer = Bukkit.getPlayerExact(loginMessage.getPlayerName());;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetPlayer == null) {
|
if (targetPlayer == null) {
|
||||||
@ -74,12 +50,12 @@ public class BungeeListener implements PluginMessageListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fail if target player is blocked because already authenticated or wrong bungeecord id
|
// fail if target player is blocked because already authenticated or wrong proxy id
|
||||||
if (targetPlayer.hasMetadata(plugin.getName())) {
|
if (targetPlayer.hasMetadata(plugin.getName())) {
|
||||||
plugin.getLog().warn("Received message {} from a blocked player {}", loginMessage, targetPlayer);
|
plugin.getLog().warn("Received message {} from a blocked player {}", loginMessage, targetPlayer);
|
||||||
} else {
|
} else {
|
||||||
UUID sourceId = loginMessage.getProxyId();
|
UUID sourceId = loginMessage.getProxyId();
|
||||||
if (plugin.getBungeeManager().isProxyAllowed(sourceId)) {
|
if (plugin.getProxyManager().isProxyAllowed(sourceId)) {
|
||||||
readMessage(targetPlayer, loginMessage);
|
readMessage(targetPlayer, loginMessage);
|
||||||
} else {
|
} else {
|
||||||
plugin.getLog().warn("Received proxy id: {} that doesn't exist in the proxy file", sourceId);
|
plugin.getLog().warn("Received proxy id: {} that doesn't exist in the proxy file", sourceId);
|
||||||
@ -91,23 +67,24 @@ public class BungeeListener implements PluginMessageListener {
|
|||||||
String playerName = message.getPlayerName();
|
String playerName = message.getPlayerName();
|
||||||
Type type = message.getType();
|
Type type = message.getType();
|
||||||
|
|
||||||
|
InetSocketAddress address = player.getAddress();
|
||||||
plugin.getLog().info("Player info {} command for {} from proxy", type, playerName);
|
plugin.getLog().info("Player info {} command for {} from proxy", type, playerName);
|
||||||
if (type == Type.LOGIN) {
|
if (type == Type.LOGIN) {
|
||||||
onLoginMessage(player, playerName);
|
onLoginMessage(player, playerName, address);
|
||||||
} else if (type == Type.REGISTER) {
|
} else if (type == Type.REGISTER) {
|
||||||
onRegisterMessage(player, playerName);
|
onRegisterMessage(player, playerName, address);
|
||||||
} else if (type == Type.CRACKED) {
|
} else if (type == Type.CRACKED) {
|
||||||
//we don't start a force login task here so update it manually
|
//we don't start a force login task here so update it manually
|
||||||
plugin.getPremiumPlayers().put(player.getUniqueId(), PremiumStatus.CRACKED);
|
plugin.getPremiumPlayers().put(player.getUniqueId(), PremiumStatus.CRACKED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onLoginMessage(Player player, String playerName) {
|
private void onLoginMessage(Player player, String playerName, InetSocketAddress address) {
|
||||||
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, true);
|
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, true);
|
||||||
startLoginTaskIfReady(player, playerSession);
|
startLoginTaskIfReady(player, playerSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onRegisterMessage(Player player, String playerName) {
|
private void onRegisterMessage(Player player, String playerName, InetSocketAddress address) {
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook();
|
AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook();
|
||||||
try {
|
try {
|
||||||
@ -124,10 +101,10 @@ public class BungeeListener implements PluginMessageListener {
|
|||||||
|
|
||||||
private void startLoginTaskIfReady(Player player, BukkitLoginSession session) {
|
private void startLoginTaskIfReady(Player player, BukkitLoginSession session) {
|
||||||
session.setVerified(true);
|
session.setVerified(true);
|
||||||
plugin.putSession(player.spigot().getRawAddress(), session);
|
plugin.getSessionManager().startLoginSession(player.getAddress(), session);
|
||||||
|
|
||||||
// only start a new login task if the join event fired earlier. This event then didn't
|
// only start a new login task if the join event fired earlier. This event then didn
|
||||||
boolean result = plugin.getBungeeManager().didJoinEventFired(player);
|
boolean result = plugin.getProxyManager().didJoinEventFired(player);
|
||||||
plugin.getLog().info("Delaying force login until join event fired?: {}", result);
|
plugin.getLog().info("Delaying force login until join event fired?: {}", result);
|
||||||
if (result) {
|
if (result) {
|
||||||
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player, session);
|
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player, session);
|
@ -1,38 +1,13 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.command;
|
package com.github.games647.fastlogin.bukkit.command;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPremiumToggleEvent;
|
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPremiumToggleEvent;
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
|
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import static com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
|
import static com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.*;
|
||||||
|
|
||||||
public class CrackedCommand extends ToggleCommand {
|
public class CrackedCommand extends ToggleCommand {
|
||||||
|
|
||||||
@ -41,10 +16,9 @@ public class CrackedCommand extends ToggleCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
String[] args) {
|
|
||||||
if (args.length == 0) {
|
if (args.length == 0) {
|
||||||
onCrackedSelf(sender);
|
onCrackedSelf(sender, command, args);
|
||||||
} else {
|
} else {
|
||||||
onCrackedOther(sender, command, args);
|
onCrackedOther(sender, command, args);
|
||||||
}
|
}
|
||||||
@ -52,7 +26,7 @@ public class CrackedCommand extends ToggleCommand {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onCrackedSelf(CommandSender sender) {
|
private void onCrackedSelf(CommandSender sender, Command cmd, String[] args) {
|
||||||
if (isConsole(sender)) {
|
if (isConsole(sender)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -61,20 +35,25 @@ public class CrackedCommand extends ToggleCommand {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: load async if
|
if (plugin.getProxyManager().isEnabled()) {
|
||||||
StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
|
sendProxyActivateMessage(sender, sender.getName(), false);
|
||||||
if (profile.isPremium()) {
|
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
|
||||||
plugin.getCore().sendLocaleMessage("remove-premium", sender);
|
|
||||||
|
|
||||||
profile.setPremium(false);
|
|
||||||
profile.setId(null);
|
|
||||||
plugin.getScheduler().runAsync(() -> {
|
|
||||||
plugin.getCore().getStorage().save(profile);
|
|
||||||
plugin.getServer().getPluginManager().callEvent(
|
|
||||||
new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_OTHER));
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
plugin.getCore().sendLocaleMessage("not-premium", sender);
|
//todo: load async if
|
||||||
|
StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
|
||||||
|
if (profile.isPremium()) {
|
||||||
|
plugin.getCore().sendLocaleMessage("remove-premium", sender);
|
||||||
|
|
||||||
|
profile.setPremium(false);
|
||||||
|
profile.setId(null);
|
||||||
|
plugin.getScheduler().runAsync(() -> {
|
||||||
|
plugin.getCore().getStorage().save(profile);
|
||||||
|
plugin.getServer().getPluginManager().callEvent(
|
||||||
|
new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_OTHER));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
plugin.getCore().sendLocaleMessage("not-premium", sender);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,6 +89,6 @@ public class CrackedCommand extends ToggleCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean forwardCrackedCommand(CommandSender sender, String target) {
|
private boolean forwardCrackedCommand(CommandSender sender, String target) {
|
||||||
return forwardBungeeCommand(sender, target, false);
|
return forwardProxyCommand(sender, target, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,19 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.command;
|
package com.github.games647.fastlogin.bukkit.command;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPremiumToggleEvent;
|
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPremiumToggleEvent;
|
||||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
|
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
import org.bukkit.command.Command;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Let users activate fast login by command. This only be accessible if
|
* Let users activate fast login by command. This only be accessible if
|
||||||
* the user has access to its account. So we can make sure that not another
|
* the user has access to it's account. So we can make sure that not another
|
||||||
* person with a paid account and the same username can steal their account.
|
* person with a paid account and the same username can steal their account.
|
||||||
*/
|
*/
|
||||||
public class PremiumCommand extends ToggleCommand {
|
public class PremiumCommand extends ToggleCommand {
|
||||||
@ -48,10 +23,9 @@ public class PremiumCommand extends ToggleCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
String[] args) {
|
|
||||||
if (args.length == 0) {
|
if (args.length == 0) {
|
||||||
onPremiumSelf(sender);
|
onPremiumSelf(sender, command, args);
|
||||||
} else {
|
} else {
|
||||||
onPremiumOther(sender, command, args);
|
onPremiumOther(sender, command, args);
|
||||||
}
|
}
|
||||||
@ -59,7 +33,7 @@ public class PremiumCommand extends ToggleCommand {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPremiumSelf(CommandSender sender) {
|
private void onPremiumSelf(CommandSender sender, Command cmd, String[] args) {
|
||||||
if (isConsole(sender)) {
|
if (isConsole(sender)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -125,6 +99,6 @@ public class PremiumCommand extends ToggleCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean forwardPremiumCommand(CommandSender sender, String target) {
|
private boolean forwardPremiumCommand(CommandSender sender, String target) {
|
||||||
return forwardBungeeCommand(sender, target, true);
|
return forwardProxyCommand(sender, target, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,11 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.command;
|
package com.github.games647.fastlogin.bukkit.command;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
||||||
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandExecutor;
|
import org.bukkit.command.CommandExecutor;
|
||||||
@ -35,8 +13,6 @@ import org.bukkit.command.CommandSender;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.plugin.messaging.PluginMessageRecipient;
|
import org.bukkit.plugin.messaging.PluginMessageRecipient;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public abstract class ToggleCommand implements CommandExecutor {
|
public abstract class ToggleCommand implements CommandExecutor {
|
||||||
|
|
||||||
protected final FastLoginBukkit plugin;
|
protected final FastLoginBukkit plugin;
|
||||||
@ -46,17 +22,17 @@ public abstract class ToggleCommand implements CommandExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean hasOtherPermission(CommandSender sender, Command cmd) {
|
protected boolean hasOtherPermission(CommandSender sender, Command cmd) {
|
||||||
if (sender.hasPermission(cmd.getPermission() + ".other")) {
|
if (!sender.hasPermission(cmd.getPermission() + ".other")) {
|
||||||
return true;
|
plugin.getCore().sendLocaleMessage("no-permission", sender);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin.getCore().sendLocaleMessage("no-permission", sender);
|
return true;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean forwardBungeeCommand(CommandSender sender, String target, boolean activate) {
|
protected boolean forwardProxyCommand(CommandSender sender, String target, boolean activate) {
|
||||||
if (plugin.getBungeeManager().isEnabled()) {
|
if (plugin.getProxyManager().isEnabled()) {
|
||||||
sendBungeeActivateMessage(sender, target, activate);
|
sendProxyActivateMessage(sender, target, activate);
|
||||||
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
|
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -74,10 +50,10 @@ public abstract class ToggleCommand implements CommandExecutor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void sendBungeeActivateMessage(CommandSender invoker, String target, boolean activate) {
|
protected void sendProxyActivateMessage(CommandSender invoker, String target, boolean activate) {
|
||||||
if (invoker instanceof PluginMessageRecipient) {
|
if (invoker instanceof PluginMessageRecipient) {
|
||||||
ChannelMessage message = new ChangePremiumMessage(target, activate, true);
|
ChannelMessage message = new ChangePremiumMessage(target, activate, true);
|
||||||
plugin.getBungeeManager().sendPluginMessage((PluginMessageRecipient) invoker, message);
|
plugin.getProxyManager().sendPluginMessage((PluginMessageRecipient) invoker, message);
|
||||||
} else {
|
} else {
|
||||||
Optional<? extends Player> optPlayer = Bukkit.getServer().getOnlinePlayers().stream().findFirst();
|
Optional<? extends Player> optPlayer = Bukkit.getServer().getOnlinePlayers().stream().findFirst();
|
||||||
if (!optPlayer.isPresent()) {
|
if (!optPlayer.isPresent()) {
|
||||||
@ -87,7 +63,7 @@ public abstract class ToggleCommand implements CommandExecutor {
|
|||||||
|
|
||||||
Player sender = optPlayer.get();
|
Player sender = optPlayer.get();
|
||||||
ChannelMessage message = new ChangePremiumMessage(target, activate, false);
|
ChannelMessage message = new ChangePremiumMessage(target, activate, false);
|
||||||
plugin.getBungeeManager().sendPluginMessage(sender, message);
|
plugin.getProxyManager().sendPluginMessage(sender, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,15 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.event;
|
package com.github.games647.fastlogin.bukkit.event;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
|
||||||
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
|
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
|
import com.github.games647.fastlogin.core.auth.LoginSession;
|
||||||
|
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
|
||||||
import org.bukkit.event.Cancellable;
|
import org.bukkit.event.Cancellable;
|
||||||
import org.bukkit.event.Event;
|
import org.bukkit.event.Event;
|
||||||
import org.bukkit.event.HandlerList;
|
import org.bukkit.event.HandlerList;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class BukkitFastLoginAutoLoginEvent extends Event implements FastLoginAutoLoginEvent, Cancellable {
|
public class BukkitFastLoginAutoLoginEvent extends Event implements FastLoginAutoLoginEvent, Cancellable {
|
||||||
|
|
||||||
private static final HandlerList HANDLERS = new HandlerList();
|
private static final HandlerList handlers = new HandlerList();
|
||||||
private final LoginSession session;
|
private final LoginSession session;
|
||||||
private final StoredProfile profile;
|
private final StoredProfile profile;
|
||||||
private boolean cancelled;
|
private boolean cancelled;
|
||||||
@ -68,11 +42,11 @@ public class BukkitFastLoginAutoLoginEvent extends Event implements FastLoginAut
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull HandlerList getHandlers() {
|
public HandlerList getHandlers() {
|
||||||
return HANDLERS;
|
return handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HandlerList getHandlerList() {
|
public static HandlerList getHandlerList() {
|
||||||
return HANDLERS;
|
return handlers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,14 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.event;
|
package com.github.games647.fastlogin.bukkit.event;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
|
||||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
|
import com.github.games647.fastlogin.core.auth.LoginSource;
|
||||||
|
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
||||||
import org.bukkit.event.Event;
|
import org.bukkit.event.Event;
|
||||||
import org.bukkit.event.HandlerList;
|
import org.bukkit.event.HandlerList;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class BukkitFastLoginPreLoginEvent extends Event implements FastLoginPreLoginEvent {
|
public class BukkitFastLoginPreLoginEvent extends Event implements FastLoginPreLoginEvent {
|
||||||
|
|
||||||
private static final HandlerList HANDLERS = new HandlerList();
|
private static final HandlerList handlers = new HandlerList();
|
||||||
private final String username;
|
private final String username;
|
||||||
private final LoginSource source;
|
private final LoginSource source;
|
||||||
private final StoredProfile profile;
|
private final StoredProfile profile;
|
||||||
@ -63,11 +37,11 @@ public class BukkitFastLoginPreLoginEvent extends Event implements FastLoginPreL
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull HandlerList getHandlers() {
|
public HandlerList getHandlers() {
|
||||||
return HANDLERS;
|
return handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HandlerList getHandlerList() {
|
public static HandlerList getHandlerList() {
|
||||||
return HANDLERS;
|
return handlers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,13 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.event;
|
package com.github.games647.fastlogin.bukkit.event;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
|
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
|
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
|
||||||
import org.bukkit.event.Event;
|
import org.bukkit.event.Event;
|
||||||
import org.bukkit.event.HandlerList;
|
import org.bukkit.event.HandlerList;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {
|
public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {
|
||||||
|
|
||||||
private static final HandlerList HANDLERS = new HandlerList();
|
private static final HandlerList handlers = new HandlerList();
|
||||||
private final StoredProfile profile;
|
private final StoredProfile profile;
|
||||||
private final PremiumToggleReason reason;
|
private final PremiumToggleReason reason;
|
||||||
|
|
||||||
@ -54,11 +28,11 @@ public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLogi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull HandlerList getHandlers() {
|
public HandlerList getHandlers() {
|
||||||
return HANDLERS;
|
return handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HandlerList getHandlerList() {
|
public static HandlerList getHandlerList() {
|
||||||
return HANDLERS;
|
return handlers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,53 +1,30 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.hook;
|
package com.github.games647.fastlogin.bukkit.hook;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||||
|
|
||||||
import fr.xephi.authme.api.v3.AuthMeApi;
|
import fr.xephi.authme.api.v3.AuthMeApi;
|
||||||
import fr.xephi.authme.events.RestoreSessionEvent;
|
import fr.xephi.authme.events.RestoreSessionEvent;
|
||||||
import fr.xephi.authme.process.Management;
|
import fr.xephi.authme.process.Management;
|
||||||
import fr.xephi.authme.process.register.executors.ApiPasswordRegisterParams;
|
import fr.xephi.authme.process.register.executors.ApiPasswordRegisterParams;
|
||||||
import fr.xephi.authme.process.register.executors.RegistrationMethod;
|
import fr.xephi.authme.process.register.executors.RegistrationMethod;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GitHub: <a href="https://github.com/Xephi/AuthMeReloaded/">...</a>
|
* GitHub: https://github.com/Xephi/AuthMeReloaded/
|
||||||
* <p>
|
* <p>
|
||||||
* Project page:
|
* Project page:
|
||||||
* <p>
|
* <p>
|
||||||
* Bukkit: <a href="https://dev.bukkit.org/bukkit-plugins/authme-reloaded/">...</a>
|
* Bukkit: https://dev.bukkit.org/bukkit-plugins/authme-reloaded/
|
||||||
* <p>
|
* <p>
|
||||||
* Spigot: <a href="https://www.spigotmc.org/resources/authme-reloaded.6269/">...</a>
|
* Spigot: https://www.spigotmc.org/resources/authme-reloaded.6269/
|
||||||
*/
|
*/
|
||||||
public class AuthMeHook implements AuthPlugin<Player>, Listener {
|
public class AuthMeHook implements AuthPlugin<Player>, Listener {
|
||||||
|
|
||||||
@ -56,11 +33,11 @@ public class AuthMeHook implements AuthPlugin<Player>, Listener {
|
|||||||
private final AuthMeApi authmeAPI;
|
private final AuthMeApi authmeAPI;
|
||||||
private Management authmeManagement;
|
private Management authmeManagement;
|
||||||
|
|
||||||
public AuthMeHook(FastLoginBukkit plugin) {
|
protected AuthMeHook(FastLoginBukkit plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.authmeAPI = AuthMeApi.getInstance();
|
this.authmeAPI = AuthMeApi.getInstance();
|
||||||
|
|
||||||
if (plugin.getCore().getConfig().getBoolean("respectIpLimit", false)) {
|
if (plugin.getConfig().getBoolean("respectIpLimit", false)) {
|
||||||
try {
|
try {
|
||||||
Field managementField = this.authmeAPI.getClass().getDeclaredField("management");
|
Field managementField = this.authmeAPI.getClass().getDeclaredField("management");
|
||||||
managementField.setAccessible(true);
|
managementField.setAccessible(true);
|
||||||
@ -75,8 +52,8 @@ public class AuthMeHook implements AuthPlugin<Player>, Listener {
|
|||||||
public void onSessionRestore(RestoreSessionEvent restoreSessionEvent) {
|
public void onSessionRestore(RestoreSessionEvent restoreSessionEvent) {
|
||||||
Player player = restoreSessionEvent.getPlayer();
|
Player player = restoreSessionEvent.getPlayer();
|
||||||
|
|
||||||
BukkitLoginSession session = plugin.getSession(player.spigot().getRawAddress());
|
StoredProfile session = plugin.getSessionManager().getPlaySession(player.getUniqueId());
|
||||||
if (session != null && session.isVerified()) {
|
if (session != null && session.isPremium()) {
|
||||||
restoreSessionEvent.setCancelled(true);
|
restoreSessionEvent.setCancelled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,28 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.hook;
|
package com.github.games647.fastlogin.bukkit.hook;
|
||||||
|
|
||||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||||
|
|
||||||
import de.st_ddt.crazylogin.CrazyLogin;
|
import de.st_ddt.crazylogin.CrazyLogin;
|
||||||
import de.st_ddt.crazylogin.data.LoginPlayerData;
|
import de.st_ddt.crazylogin.data.LoginPlayerData;
|
||||||
import de.st_ddt.crazylogin.databases.CrazyLoginDataDatabase;
|
import de.st_ddt.crazylogin.databases.CrazyLoginDataDatabase;
|
||||||
import de.st_ddt.crazylogin.listener.PlayerListener;
|
import de.st_ddt.crazylogin.listener.PlayerListener;
|
||||||
import de.st_ddt.crazylogin.metadata.Authenticated;
|
import de.st_ddt.crazylogin.metadata.Authenticated;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GitHub: <a href="https://github.com/ST-DDT/CrazyLogin">...</a>
|
* GitHub: https://github.com/ST-DDT/CrazyLogin
|
||||||
* <p>
|
* <p>
|
||||||
* Project page:
|
* Project page:
|
||||||
* <p>
|
* <p>
|
||||||
* Bukkit: <a href="https://dev.bukkit.org/server-mods/crazylogin/">...</a>
|
* Bukkit: https://dev.bukkit.org/server-mods/crazylogin/
|
||||||
*/
|
*/
|
||||||
public class CrazyLoginHook implements AuthPlugin<Player> {
|
public class CrazyLoginHook implements AuthPlugin<Player> {
|
||||||
|
|
||||||
@ -55,7 +31,7 @@ public class CrazyLoginHook implements AuthPlugin<Player> {
|
|||||||
private final CrazyLogin crazyLoginPlugin;
|
private final CrazyLogin crazyLoginPlugin;
|
||||||
private final PlayerListener playerListener;
|
private final PlayerListener playerListener;
|
||||||
|
|
||||||
public CrazyLoginHook(FastLoginBukkit plugin) {
|
protected CrazyLoginHook(FastLoginBukkit plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
||||||
crazyLoginPlugin = CrazyLogin.getPlugin();
|
crazyLoginPlugin = CrazyLogin.getPlugin();
|
||||||
@ -120,11 +96,11 @@ public class CrazyLoginHook implements AuthPlugin<Player> {
|
|||||||
public boolean forceRegister(Player player, String password) {
|
public boolean forceRegister(Player player, String password) {
|
||||||
CrazyLoginDataDatabase crazyDatabase = crazyLoginPlugin.getCrazyDatabase();
|
CrazyLoginDataDatabase crazyDatabase = crazyLoginPlugin.getCrazyDatabase();
|
||||||
|
|
||||||
//this executes a sql query and accesses only thread safe collections, so we can run it async
|
//this executes a sql query and accesses only thread safe collections so we can run it async
|
||||||
LoginPlayerData playerData = crazyLoginPlugin.getPlayerData(player.getName());
|
LoginPlayerData playerData = crazyLoginPlugin.getPlayerData(player.getName());
|
||||||
if (playerData == null) {
|
if (playerData == null) {
|
||||||
//create a fake account - this will be saved to the database with the password=FAILEDLOADING
|
//create a fake account - this will be saved to the database with the password=FAILEDLOADING
|
||||||
//user cannot log in with that password unless the admin uses plain text
|
//user cannot login with that password unless the admin uses plain text
|
||||||
//this automatically marks the player as logged in
|
//this automatically marks the player as logged in
|
||||||
crazyDatabase.save(new LoginPlayerData(player));
|
crazyDatabase.save(new LoginPlayerData(player));
|
||||||
return forceLogin(player);
|
return forceLogin(player);
|
||||||
@ -133,8 +109,15 @@ public class CrazyLoginHook implements AuthPlugin<Player> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected PlayerListener getListener() {
|
private PlayerListener getListener() {
|
||||||
FieldAccessor accessor = Accessors.getFieldAccessor(crazyLoginPlugin.getClass(), PlayerListener.class, true);
|
PlayerListener listener;
|
||||||
return (PlayerListener) accessor.get(crazyLoginPlugin);
|
try {
|
||||||
|
listener = (PlayerListener) FieldUtils.readField(crazyLoginPlugin, "playerListener", true);
|
||||||
|
} catch (IllegalAccessException ex) {
|
||||||
|
plugin.getLog().error("Failed to get the listener instance for auto login", ex);
|
||||||
|
listener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return listener;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,46 +1,16 @@
|
|||||||
/*
|
package com.github.games647.fastlogin.bukkit.hook;
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.task;
|
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.bukkit.hook.AuthMeHook;
|
|
||||||
import com.github.games647.fastlogin.bukkit.hook.CrazyLoginHook;
|
|
||||||
import com.github.games647.fastlogin.bukkit.hook.LogItHook;
|
|
||||||
import com.github.games647.fastlogin.bukkit.hook.LoginSecurityHook;
|
|
||||||
import com.github.games647.fastlogin.bukkit.hook.UltraAuthHook;
|
|
||||||
import com.github.games647.fastlogin.bukkit.hook.XAuthHook;
|
|
||||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
|
||||||
public class DelayedAuthHook implements Runnable {
|
public class DelayedAuthHook implements Runnable {
|
||||||
|
|
||||||
private final FastLoginBukkit plugin;
|
private final FastLoginBukkit plugin;
|
||||||
@ -52,18 +22,14 @@ public class DelayedAuthHook implements Runnable {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
boolean hookFound = isHookFound();
|
boolean hookFound = isHookFound();
|
||||||
if (plugin.getBungeeManager().isEnabled()) {
|
if (plugin.getProxyManager().isEnabled()) {
|
||||||
plugin.getLog().info("BungeeCord setting detected. No auth plugin is required");
|
plugin.getLog().info("Proxy setting detected. No auth plugin is required");
|
||||||
} else if (!hookFound) {
|
} else if (!hookFound) {
|
||||||
plugin.getLog().warn("No auth plugin were found by this plugin "
|
plugin.getLog().warn("No auth plugin were found by this plugin "
|
||||||
+ "(other plugins could hook into this after the initialization of this plugin)"
|
+ "(other plugins could hook into this after the initialization of this plugin)"
|
||||||
+ "and BungeeCord is deactivated. "
|
+ "and BungeeCord (or similar proxies) is deactivated. "
|
||||||
+ "Either one or both of the checks have to pass in order to use this plugin");
|
+ "Either one or both of the checks have to pass in order to use this plugin");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hookFound) {
|
|
||||||
plugin.markInitialized();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isHookFound() {
|
private boolean isHookFound() {
|
||||||
@ -92,13 +58,13 @@ public class DelayedAuthHook implements Runnable {
|
|||||||
|
|
||||||
private AuthPlugin<Player> getAuthHook() {
|
private AuthPlugin<Player> getAuthHook() {
|
||||||
try {
|
try {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
List<Class<? extends AuthPlugin<Player>>> hooks = Arrays.asList(AuthMeHook.class,
|
List<Class<? extends AuthPlugin<Player>>> hooks = Arrays.asList(AuthMeHook.class,
|
||||||
CrazyLoginHook.class, LogItHook.class, LoginSecurityHook.class, UltraAuthHook.class,
|
CrazyLoginHook.class, LogItHook.class, LoginSecurityHook.class,
|
||||||
XAuthHook.class);
|
SodionAuthHook.class, UltraAuthHook.class, xAuthHook.class);
|
||||||
|
|
||||||
for (Class<? extends AuthPlugin<Player>> clazz : hooks) {
|
for (Class<? extends AuthPlugin<Player>> clazz : hooks) {
|
||||||
String pluginName = clazz.getSimpleName();
|
String pluginName = clazz.getSimpleName().replace("Hook", "");
|
||||||
pluginName = pluginName.substring(0, pluginName.length() - "Hook".length());
|
|
||||||
//uses only member classes which uses AuthPlugin interface (skip interfaces)
|
//uses only member classes which uses AuthPlugin interface (skip interfaces)
|
||||||
if (Bukkit.getPluginManager().isPluginEnabled(pluginName)) {
|
if (Bukkit.getPluginManager().isPluginEnabled(pluginName)) {
|
||||||
//check only for enabled plugins. A single plugin could be disabled by plugin managers
|
//check only for enabled plugins. A single plugin could be disabled by plugin managers
|
||||||
@ -112,7 +78,7 @@ public class DelayedAuthHook implements Runnable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AuthPlugin<Player> newInstance(Class<? extends AuthPlugin<Player>> clazz)
|
private AuthPlugin<Player> newInstance(Class<? extends AuthPlugin<Player>> clazz)
|
||||||
throws ReflectiveOperationException {
|
throws ReflectiveOperationException {
|
||||||
try {
|
try {
|
||||||
Constructor<? extends AuthPlugin<Player>> cons = clazz.getDeclaredConstructor(FastLoginBukkit.class);
|
Constructor<? extends AuthPlugin<Player>> cons = clazz.getDeclaredConstructor(FastLoginBukkit.class);
|
@ -1,42 +1,19 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.hook;
|
package com.github.games647.fastlogin.bukkit.hook;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||||
|
|
||||||
import io.github.lucaseasedup.logit.CancelledState;
|
import io.github.lucaseasedup.logit.CancelledState;
|
||||||
import io.github.lucaseasedup.logit.LogItCore;
|
import io.github.lucaseasedup.logit.LogItCore;
|
||||||
import io.github.lucaseasedup.logit.account.Account;
|
import io.github.lucaseasedup.logit.account.Account;
|
||||||
import io.github.lucaseasedup.logit.session.SessionManager;
|
import io.github.lucaseasedup.logit.session.SessionManager;
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GitHub: <a href="https://github.com/XziomekX/LogIt">...</a>
|
* GitHub: https://github.com/XziomekX/LogIt
|
||||||
* <p>
|
* <p>
|
||||||
* Project page:
|
* Project page:
|
||||||
* <p>
|
* <p>
|
||||||
@ -48,7 +25,7 @@ public class LogItHook implements AuthPlugin<Player> {
|
|||||||
|
|
||||||
private final FastLoginBukkit plugin;
|
private final FastLoginBukkit plugin;
|
||||||
|
|
||||||
public LogItHook(FastLoginBukkit plugin) {
|
protected LogItHook(FastLoginBukkit plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,28 +1,3 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.hook;
|
package com.github.games647.fastlogin.bukkit.hook;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
@ -32,22 +7,23 @@ import com.lenis0012.bukkit.loginsecurity.session.AuthService;
|
|||||||
import com.lenis0012.bukkit.loginsecurity.session.PlayerSession;
|
import com.lenis0012.bukkit.loginsecurity.session.PlayerSession;
|
||||||
import com.lenis0012.bukkit.loginsecurity.session.action.LoginAction;
|
import com.lenis0012.bukkit.loginsecurity.session.action.LoginAction;
|
||||||
import com.lenis0012.bukkit.loginsecurity.session.action.RegisterAction;
|
import com.lenis0012.bukkit.loginsecurity.session.action.RegisterAction;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GitHub: <a href="https://github.com/lenis0012/LoginSecurity-2">...</a>
|
* GitHub: https://github.com/lenis0012/LoginSecurity-2
|
||||||
* <p>
|
* <p>
|
||||||
* Project page:
|
* Project page:
|
||||||
* <p>
|
* <p>
|
||||||
* Bukkit: <a href="https://dev.bukkit.org/bukkit-plugins/loginsecurity/">...</a>
|
* Bukkit: https://dev.bukkit.org/bukkit-plugins/loginsecurity/
|
||||||
* <p>
|
* <p>
|
||||||
* Spigot: <a href="https://www.spigotmc.org/resources/loginsecurity.19362/">...</a>
|
* Spigot: https://www.spigotmc.org/resources/loginsecurity.19362/
|
||||||
*/
|
*/
|
||||||
public class LoginSecurityHook implements AuthPlugin<Player> {
|
public class LoginSecurityHook implements AuthPlugin<Player> {
|
||||||
|
|
||||||
private final FastLoginBukkit plugin;
|
private final FastLoginBukkit plugin;
|
||||||
|
|
||||||
public LoginSecurityHook(FastLoginBukkit plugin) {
|
protected LoginSecurityHook(FastLoginBukkit plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.github.games647.fastlogin.bukkit.hook;
|
||||||
|
|
||||||
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
|
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import red.mohist.sodionauth.bukkit.implementation.BukkitPlayer;
|
||||||
|
import red.mohist.sodionauth.core.SodionAuthApi;
|
||||||
|
import red.mohist.sodionauth.core.exception.AuthenticatedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GitHub: https://github.com/Mohist-Community/SodionAuth
|
||||||
|
* <p>
|
||||||
|
* Project page:
|
||||||
|
* <p>
|
||||||
|
* Bukkit: Unknown
|
||||||
|
* <p>
|
||||||
|
* Spigot: Unknown
|
||||||
|
*/
|
||||||
|
public class SodionAuthHook implements AuthPlugin<Player> {
|
||||||
|
|
||||||
|
private final FastLoginBukkit plugin;
|
||||||
|
|
||||||
|
protected SodionAuthHook(FastLoginBukkit plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean forceLogin(Player player) {
|
||||||
|
try {
|
||||||
|
SodionAuthApi.login(new BukkitPlayer(player));
|
||||||
|
} catch (AuthenticatedException e) {
|
||||||
|
plugin.getLog().warn(ALREADY_AUTHENTICATED, player);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean forceRegister(Player player, String password) {
|
||||||
|
try{
|
||||||
|
return SodionAuthApi.register(new BukkitPlayer(player), password);
|
||||||
|
} catch (UnsupportedOperationException e){
|
||||||
|
plugin.getLog().warn("Currently SodionAuth is not accepting forceRegister, " +
|
||||||
|
"It may be caused by unsupported AuthBackend");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRegistered(String playerName) {
|
||||||
|
return SodionAuthApi.isRegistered(playerName);
|
||||||
|
}
|
||||||
|
}
|
@ -1,52 +1,31 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.hook;
|
package com.github.games647.fastlogin.bukkit.hook;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import ultraauth.api.UltraAuthAPI;
|
|
||||||
import ultraauth.managers.PlayerManager;
|
|
||||||
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import ultraauth.api.UltraAuthAPI;
|
||||||
|
import ultraauth.main.Main;
|
||||||
|
import ultraauth.managers.PlayerManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project page:
|
* Project page:
|
||||||
* <p>
|
* <p>
|
||||||
* Bukkit: <a href="https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/">...</a>
|
* Bukkit: https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/
|
||||||
* <p>
|
* <p>
|
||||||
* Spigot: <a href="https://www.spigotmc.org/resources/ultraauth.17044/">...</a>
|
* Spigot: https://www.spigotmc.org/resources/ultraauth.17044/
|
||||||
*/
|
*/
|
||||||
public class UltraAuthHook implements AuthPlugin<Player> {
|
public class UltraAuthHook implements AuthPlugin<Player> {
|
||||||
|
|
||||||
|
private final Plugin ultraAuthPlugin = Main.main;
|
||||||
private final FastLoginBukkit plugin;
|
private final FastLoginBukkit plugin;
|
||||||
|
|
||||||
public UltraAuthHook(FastLoginBukkit plugin) {
|
protected UltraAuthHook(FastLoginBukkit plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,53 +1,30 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.hook;
|
package com.github.games647.fastlogin.bukkit.hook;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||||
|
|
||||||
import de.luricos.bukkit.xAuth.xAuth;
|
import de.luricos.bukkit.xAuth.xAuth;
|
||||||
import de.luricos.bukkit.xAuth.xAuthPlayer;
|
import de.luricos.bukkit.xAuth.xAuthPlayer;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GitHub: <a href="https://github.com/LycanDevelopment/xAuth/">...</a>
|
* GitHub: https://github.com/LycanDevelopment/xAuth/
|
||||||
* <p>
|
* <p>
|
||||||
* Project page:
|
* Project page:
|
||||||
* <p>
|
* <p>
|
||||||
* Bukkit: <a href="https://dev.bukkit.org/bukkit-plugins/xauth/">...</a>
|
* Bukkit: https://dev.bukkit.org/bukkit-plugins/xauth/
|
||||||
*/
|
*/
|
||||||
public class XAuthHook implements AuthPlugin<Player> {
|
public class xAuthHook implements AuthPlugin<Player> {
|
||||||
|
|
||||||
private final xAuth xAuthPlugin = xAuth.getPlugin();
|
private final xAuth xAuthPlugin = xAuth.getPlugin();
|
||||||
private final FastLoginBukkit plugin;
|
private final FastLoginBukkit plugin;
|
||||||
|
|
||||||
public XAuthHook(FastLoginBukkit plugin) {
|
protected xAuthHook(FastLoginBukkit plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
@ -1,35 +1,9 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener;
|
package com.github.games647.fastlogin.bukkit.listener;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
import com.github.games647.fastlogin.bukkit.auth.BukkitLoginSession;
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.bukkit.task.FloodgateAuthTask;
|
import com.github.games647.fastlogin.bukkit.ForceLoginTask;
|
||||||
import com.github.games647.fastlogin.bukkit.task.ForceLoginTask;
|
|
||||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
@ -37,13 +11,10 @@ import org.bukkit.event.EventPriority;
|
|||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
import org.bukkit.event.player.PlayerLoginEvent;
|
import org.bukkit.event.player.PlayerLoginEvent;
|
||||||
import org.bukkit.event.player.PlayerLoginEvent.Result;
|
|
||||||
import org.bukkit.event.player.PlayerQuitEvent;
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
import org.bukkit.metadata.Metadatable;
|
|
||||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This listener tells authentication plugins weather the player has a premium account. So the
|
* This listener tells authentication plugins if the player has a premium account and we checked it successfully. So the
|
||||||
* plugin can skip authentication.
|
* plugin can skip authentication.
|
||||||
*/
|
*/
|
||||||
public class ConnectionListener implements Listener {
|
public class ConnectionListener implements Listener {
|
||||||
@ -59,9 +30,6 @@ public class ConnectionListener implements Listener {
|
|||||||
@EventHandler(priority = EventPriority.LOWEST)
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
public void onPlayerLogin(PlayerLoginEvent loginEvent) {
|
public void onPlayerLogin(PlayerLoginEvent loginEvent) {
|
||||||
removeBlockedStatus(loginEvent.getPlayer());
|
removeBlockedStatus(loginEvent.getPlayer());
|
||||||
if (loginEvent.getResult() == Result.ALLOWED && !plugin.isServerFullyStarted()) {
|
|
||||||
loginEvent.disallow(Result.KICK_OTHER, plugin.getCore().getMessage("not-started"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true)
|
@EventHandler(ignoreCancelled = true)
|
||||||
@ -69,43 +37,21 @@ public class ConnectionListener implements Listener {
|
|||||||
Player player = joinEvent.getPlayer();
|
Player player = joinEvent.getPlayer();
|
||||||
|
|
||||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||||
delayForceLogin(player);
|
// session exists so the player is ready for force login
|
||||||
|
// cases: Paper (firing proxy message before PlayerJoinEvent) or not running proxy and already
|
||||||
|
// having the login session from the login process
|
||||||
|
BukkitLoginSession session = plugin.getSessionManager().getLoginSession(player.getAddress());
|
||||||
|
if (session != null) {
|
||||||
|
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player, session);
|
||||||
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, forceLoginTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.getProxyManager().markJoinEventFired(player);
|
||||||
// delay the login process to let auth plugins initialize the player
|
// delay the login process to let auth plugins initialize the player
|
||||||
// Magic number however as there is no direct event from those plugins
|
// Magic number however as there is no direct event from those plugins
|
||||||
}, DELAY_LOGIN);
|
}, DELAY_LOGIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void delayForceLogin(Player player) {
|
|
||||||
// session exists so the player is ready for force login
|
|
||||||
// cases: Paper (firing BungeeCord message before PlayerJoinEvent) or not running BungeeCord and already
|
|
||||||
// having the login session from the login process
|
|
||||||
BukkitLoginSession session = plugin.getSession(player.spigot().getRawAddress());
|
|
||||||
|
|
||||||
if (session == null) {
|
|
||||||
// Floodgate players usually don't have a session at this point
|
|
||||||
// exception: if force login by bungee message had been delayed
|
|
||||||
FloodgateService floodgateService = plugin.getFloodgateService();
|
|
||||||
if (floodgateService != null) {
|
|
||||||
FloodgatePlayer floodgatePlayer = floodgateService.getBedrockPlayer(player.getUniqueId());
|
|
||||||
if (floodgatePlayer != null) {
|
|
||||||
Runnable floodgateAuthTask = new FloodgateAuthTask(plugin.getCore(), player, floodgatePlayer);
|
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, floodgateAuthTask);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String sessionId = plugin.getSessionId(player.spigot().getRawAddress());
|
|
||||||
plugin.getLog().info("No on-going login session for player: {} with ID {}. ", player, sessionId);
|
|
||||||
plugin.getLog().info("Setups using Minecraft proxies will start delayed "
|
|
||||||
+ "when the command from the proxy is received");
|
|
||||||
} else {
|
|
||||||
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player, session);
|
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, forceLoginTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
plugin.getBungeeManager().markJoinEventFired(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerQuit(PlayerQuitEvent quitEvent) {
|
public void onPlayerQuit(PlayerQuitEvent quitEvent) {
|
||||||
Player player = quitEvent.getPlayer();
|
Player player = quitEvent.getPlayer();
|
||||||
@ -113,10 +59,10 @@ public class ConnectionListener implements Listener {
|
|||||||
removeBlockedStatus(player);
|
removeBlockedStatus(player);
|
||||||
plugin.getCore().getPendingConfirms().remove(player.getUniqueId());
|
plugin.getCore().getPendingConfirms().remove(player.getUniqueId());
|
||||||
plugin.getPremiumPlayers().remove(player.getUniqueId());
|
plugin.getPremiumPlayers().remove(player.getUniqueId());
|
||||||
plugin.getBungeeManager().cleanup(player);
|
plugin.getProxyManager().cleanup(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeBlockedStatus(Metadatable player) {
|
private void removeBlockedStatus(Player player) {
|
||||||
player.removeMetadata(plugin.getName(), plugin);
|
player.removeMetadata(plugin.getName(), plugin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener;
|
|
||||||
|
|
||||||
import com.destroystokyo.paper.profile.ProfileProperty;
|
|
||||||
import com.github.games647.craftapi.model.skin.Textures;
|
|
||||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.EventPriority;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
|
|
||||||
import org.bukkit.event.player.AsyncPlayerPreLoginEvent.Result;
|
|
||||||
|
|
||||||
public class PaperCacheListener implements Listener {
|
|
||||||
|
|
||||||
private final FastLoginBukkit plugin;
|
|
||||||
|
|
||||||
public PaperCacheListener(final FastLoginBukkit plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST)
|
|
||||||
//if paper is used - player skin must be set at pre login, otherwise user cache is used
|
|
||||||
// user cache makes premium name change basically impossible
|
|
||||||
public void onAsyncPlayerPreLogin(AsyncPlayerPreLoginEvent event) {
|
|
||||||
if (event.getLoginResult() != Result.ALLOWED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// event gives us only IP, not the port, so we need to loop through all the sessions
|
|
||||||
for (BukkitLoginSession session : plugin.getLoginSessions().values()) {
|
|
||||||
if (!event.getName().equals(session.getUsername())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
session.getSkin().ifPresent(skin -> event.getPlayerProfile().setProperty(new ProfileProperty(Textures.KEY,
|
|
||||||
skin.getValue(), skin.getSignature())));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.github.games647.fastlogin.bukkit.listener;
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.profile.ProfileProperty;
|
||||||
|
import com.github.games647.craftapi.model.skin.Textures;
|
||||||
|
import com.github.games647.fastlogin.bukkit.auth.BukkitLoginSession;
|
||||||
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
|
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
|
||||||
|
import org.bukkit.event.player.AsyncPlayerPreLoginEvent.Result;
|
||||||
|
|
||||||
|
public class PaperPreLoginListener implements Listener {
|
||||||
|
|
||||||
|
private final FastLoginBukkit plugin;
|
||||||
|
|
||||||
|
public PaperPreLoginListener(final FastLoginBukkit plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST)
|
||||||
|
//if paper is used - player skin must be set at pre login, otherwise usercache is used
|
||||||
|
//using usercache makes premium name change basically impossible
|
||||||
|
public void onAsyncPlayerPreLogin(AsyncPlayerPreLoginEvent event) {
|
||||||
|
if (event.getLoginResult() != Result.ALLOWED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// event gives us only IP, not the port, so we need to loop through all the sessions
|
||||||
|
for (BukkitLoginSession session : plugin.getSessionManager().getLoginSessions().values()) {
|
||||||
|
if (!event.getName().equals(session.getUsername())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getSkin().ifPresent(skin -> event.getPlayerProfile().setProperty(new ProfileProperty(Textures.KEY,
|
||||||
|
skin.getValue(), skin.getSignature())));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,226 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
|
||||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
|
||||||
import com.google.common.hash.Hasher;
|
|
||||||
import com.google.common.hash.Hashing;
|
|
||||||
import com.google.common.io.Resources;
|
|
||||||
import com.google.common.primitives.Longs;
|
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.KeyFactory;
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.KeyPairGenerator;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.Signature;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
|
||||||
import java.security.spec.X509EncodedKeySpec;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.Base64.Encoder;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encryption and decryption minecraft util for connection between servers
|
|
||||||
* and paid Minecraft account clients.
|
|
||||||
*/
|
|
||||||
final class EncryptionUtil {
|
|
||||||
|
|
||||||
public static final int VERIFY_TOKEN_LENGTH = 4;
|
|
||||||
public static final String KEY_PAIR_ALGORITHM = "RSA";
|
|
||||||
|
|
||||||
private static final int RSA_LENGTH = 2_048;
|
|
||||||
|
|
||||||
private static final PublicKey MOJANG_SESSION_KEY;
|
|
||||||
private static final int LINE_LENGTH = 76;
|
|
||||||
private static final Encoder KEY_ENCODER = Base64.getMimeEncoder(
|
|
||||||
LINE_LENGTH, "\n".getBytes(StandardCharsets.UTF_8)
|
|
||||||
);
|
|
||||||
private static final int MILLISECOND_SIZE = 8;
|
|
||||||
private static final int UUID_SIZE = 2 * MILLISECOND_SIZE;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
MOJANG_SESSION_KEY = loadMojangSessionKey();
|
|
||||||
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException ex) {
|
|
||||||
throw new RuntimeException("Failed to load Mojang session key", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private EncryptionUtil() {
|
|
||||||
throw new RuntimeException("No instantiation of utility classes allowed");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate an RSA key pair
|
|
||||||
*
|
|
||||||
* @return The RSA key pair.
|
|
||||||
*/
|
|
||||||
public static KeyPair generateKeyPair() {
|
|
||||||
try {
|
|
||||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_PAIR_ALGORITHM);
|
|
||||||
|
|
||||||
keyPairGenerator.initialize(RSA_LENGTH);
|
|
||||||
return keyPairGenerator.generateKeyPair();
|
|
||||||
} catch (NoSuchAlgorithmException nosuchalgorithmexception) {
|
|
||||||
// Should be existing in every vm
|
|
||||||
throw new ExceptionInInitializerError(nosuchalgorithmexception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a random token. This is used to verify that we are communicating with the same player
|
|
||||||
* in a login session.
|
|
||||||
*
|
|
||||||
* @param random random generator
|
|
||||||
* @return a token with 4 bytes long
|
|
||||||
*/
|
|
||||||
public static byte[] generateVerifyToken(Random random) {
|
|
||||||
byte[] token = new byte[VERIFY_TOKEN_LENGTH];
|
|
||||||
random.nextBytes(token);
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate the server id based on client and server data.
|
|
||||||
*
|
|
||||||
* @param serverId session for the current login attempt
|
|
||||||
* @param sharedSecret shared secret between the client and the server
|
|
||||||
* @param publicKey public key of the server
|
|
||||||
* @return the server id formatted as a hexadecimal string.
|
|
||||||
*/
|
|
||||||
public static String getServerIdHashString(String serverId, SecretKey sharedSecret, PublicKey publicKey) {
|
|
||||||
byte[] serverHash = getServerIdHash(serverId, publicKey, sharedSecret);
|
|
||||||
return (new BigInteger(serverHash)).toString(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts the content and extracts the key spec.
|
|
||||||
*
|
|
||||||
* @param privateKey private server key
|
|
||||||
* @param sharedKey the encrypted shared key
|
|
||||||
* @return shared secret key
|
|
||||||
*/
|
|
||||||
public static SecretKey decryptSharedKey(PrivateKey privateKey, byte[] sharedKey)
|
|
||||||
throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException,
|
|
||||||
BadPaddingException, InvalidKeyException {
|
|
||||||
return new SecretKeySpec(decrypt(privateKey, sharedKey), "AES");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean verifyClientKey(ClientPublicKey clientKey, Instant verifyTimestamp, UUID premiumId)
|
|
||||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
|
||||||
if (clientKey.isExpired(verifyTimestamp)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Signature verifier = Signature.getInstance("SHA1withRSA");
|
|
||||||
// key of the signer
|
|
||||||
verifier.initVerify(MOJANG_SESSION_KEY);
|
|
||||||
verifier.update(toSignable(clientKey, premiumId));
|
|
||||||
return verifier.verify(clientKey.signature());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] toSignable(ClientPublicKey clientPublicKey, UUID ownerPremiumId) {
|
|
||||||
if (ownerPremiumId == null) {
|
|
||||||
long expiry = clientPublicKey.expiry().toEpochMilli();
|
|
||||||
String encoded = KEY_ENCODER.encodeToString(clientPublicKey.key().getEncoded());
|
|
||||||
return (expiry + "-----BEGIN RSA PUBLIC KEY-----\n" + encoded + "\n-----END RSA PUBLIC KEY-----\n")
|
|
||||||
.getBytes(StandardCharsets.US_ASCII);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] keyData = clientPublicKey.key().getEncoded();
|
|
||||||
return ByteBuffer.allocate(keyData.length + UUID_SIZE + MILLISECOND_SIZE)
|
|
||||||
.putLong(ownerPremiumId.getMostSignificantBits())
|
|
||||||
.putLong(ownerPremiumId.getLeastSignificantBits())
|
|
||||||
.putLong(clientPublicKey.expiry().toEpochMilli())
|
|
||||||
.put(keyData)
|
|
||||||
.array();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean verifyNonce(byte[] expected, PrivateKey decryptionKey, byte[] encryptedNonce)
|
|
||||||
throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException,
|
|
||||||
BadPaddingException, InvalidKeyException {
|
|
||||||
byte[] decryptedNonce = decrypt(decryptionKey, encryptedNonce);
|
|
||||||
return Arrays.equals(expected, decryptedNonce);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean verifySignedNonce(byte[] nonce, PublicKey clientKey, long signatureSalt, byte[] signature)
|
|
||||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
|
||||||
Signature verifier = Signature.getInstance("SHA256withRSA");
|
|
||||||
// key of the signer
|
|
||||||
verifier.initVerify(clientKey);
|
|
||||||
|
|
||||||
verifier.update(nonce);
|
|
||||||
verifier.update(Longs.toByteArray(signatureSalt));
|
|
||||||
return verifier.verify(signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PublicKey loadMojangSessionKey()
|
|
||||||
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
|
|
||||||
val keyUrl = FastLoginBukkit.class.getClassLoader().getResource("yggdrasil_session_pubkey.der");
|
|
||||||
val keyData = Resources.toByteArray(keyUrl);
|
|
||||||
val keySpec = new X509EncodedKeySpec(keyData);
|
|
||||||
|
|
||||||
return KeyFactory.getInstance("RSA").generatePublic(keySpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] decrypt(PrivateKey key, byte[] data)
|
|
||||||
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
|
|
||||||
IllegalBlockSizeException, BadPaddingException {
|
|
||||||
Cipher cipher = Cipher.getInstance(key.getAlgorithm());
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, key);
|
|
||||||
return cipher.doFinal(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] getServerIdHash(String sessionId, PublicKey publicKey, SecretKey sharedSecret) {
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
Hasher hasher = Hashing.sha1().newHasher();
|
|
||||||
|
|
||||||
hasher.putBytes(sessionId.getBytes(StandardCharsets.ISO_8859_1));
|
|
||||||
hasher.putBytes(sharedSecret.getEncoded());
|
|
||||||
hasher.putBytes(publicKey.getEncoded());
|
|
||||||
|
|
||||||
return hasher.hash().asBytes();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,329 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
|
||||||
|
|
||||||
import com.comphenix.protocol.PacketType;
|
|
||||||
import com.comphenix.protocol.ProtocolLibrary;
|
|
||||||
import com.comphenix.protocol.events.PacketAdapter;
|
|
||||||
import com.comphenix.protocol.events.PacketContainer;
|
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
|
||||||
import com.comphenix.protocol.injector.PacketFilterManager;
|
|
||||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
|
||||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
|
||||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
|
||||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
|
||||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
|
||||||
import com.comphenix.protocol.wrappers.Converters;
|
|
||||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
|
||||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
|
||||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
|
||||||
import com.github.games647.fastlogin.core.antibot.AntiBotService;
|
|
||||||
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
|
|
||||||
import com.mojang.datafixers.util.Either;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
|
||||||
import io.netty.util.AttributeKey;
|
|
||||||
import lombok.val;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import static com.comphenix.protocol.PacketType.Login.Client.ENCRYPTION_BEGIN;
|
|
||||||
import static com.comphenix.protocol.PacketType.Login.Client.START;
|
|
||||||
|
|
||||||
public class ProtocolLibListener extends PacketAdapter {
|
|
||||||
|
|
||||||
private final FastLoginBukkit plugin;
|
|
||||||
private final PlayerInjectionHandler handler;
|
|
||||||
|
|
||||||
//just create a new once on plugin enable. This used for verify token generation
|
|
||||||
private final SecureRandom random = new SecureRandom();
|
|
||||||
private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
|
|
||||||
private final AntiBotService antiBotService;
|
|
||||||
|
|
||||||
private final boolean verifyClientKeys;
|
|
||||||
|
|
||||||
public ProtocolLibListener(FastLoginBukkit plugin, AntiBotService antiBotService, boolean verifyClientKeys) {
|
|
||||||
//run async in order to not block the server, because we are making api calls to Mojang
|
|
||||||
super(params()
|
|
||||||
.plugin(plugin)
|
|
||||||
.types(START, ENCRYPTION_BEGIN)
|
|
||||||
.optionAsync());
|
|
||||||
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.antiBotService = antiBotService;
|
|
||||||
this.verifyClientKeys = verifyClientKeys;
|
|
||||||
this.handler = getHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void register(FastLoginBukkit plugin, AntiBotService antiBotService, boolean verifyClientKeys) {
|
|
||||||
// they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError
|
|
||||||
// TODO: make synchronous processing, but do web or database requests async
|
|
||||||
ProtocolLibrary.getProtocolManager()
|
|
||||||
.getAsynchronousManager()
|
|
||||||
.registerAsyncHandler(new ProtocolLibListener(plugin, antiBotService, verifyClientKeys))
|
|
||||||
.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPacketReceiving(PacketEvent packetEvent) {
|
|
||||||
if (packetEvent.isCancelled()
|
|
||||||
|| plugin.getCore().getAuthPluginHook() == null
|
|
||||||
|| !plugin.isServerFullyStarted()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
plugin.getLog().info("New packet {} from {}", packetEvent.getPacketType(), packetEvent.getPlayer());
|
|
||||||
|
|
||||||
Player sender = packetEvent.getPlayer();
|
|
||||||
PacketType packetType = packetEvent.getPacketType();
|
|
||||||
if (packetType == START) {
|
|
||||||
|
|
||||||
if (plugin.getFloodgateService() != null) {
|
|
||||||
boolean success = processFloodgateTasks(packetEvent);
|
|
||||||
// don't continue execution if the player was kicked by Floodgate
|
|
||||||
if (!success) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PacketContainer packet = packetEvent.getPacket();
|
|
||||||
|
|
||||||
InetSocketAddress address = sender.getAddress();
|
|
||||||
String username = getUsername(packet);
|
|
||||||
|
|
||||||
Action action = antiBotService.onIncomingConnection(address, username);
|
|
||||||
switch (action) {
|
|
||||||
case Ignore:
|
|
||||||
// just ignore
|
|
||||||
return;
|
|
||||||
case Block:
|
|
||||||
String message = plugin.getCore().getMessage("kick-antibot");
|
|
||||||
sender.kickPlayer(message);
|
|
||||||
break;
|
|
||||||
case Continue:
|
|
||||||
default:
|
|
||||||
//player.getName() won't work at this state
|
|
||||||
onLoginStart(packetEvent, sender, username);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
onEncryptionBegin(packetEvent, sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onEncryptionBegin(PacketEvent packetEvent, Player sender) {
|
|
||||||
byte[] sharedSecret = packetEvent.getPacket().getByteArrays().read(0);
|
|
||||||
|
|
||||||
BukkitLoginSession session = plugin.getSession(sender.getAddress());
|
|
||||||
if (session == null) {
|
|
||||||
plugin.getLog().warn("Profile {} tried to send encryption response at invalid state", sender.getAddress());
|
|
||||||
sender.kickPlayer(plugin.getCore().getMessage("invalid-request"));
|
|
||||||
} else {
|
|
||||||
byte[] expectedVerifyToken = session.getVerifyToken();
|
|
||||||
if (verifyNonce(sender, packetEvent.getPacket(), session.getClientPublicKey(), expectedVerifyToken)) {
|
|
||||||
packetEvent.getAsyncMarker().incrementProcessingDelay();
|
|
||||||
|
|
||||||
Runnable verifyTask = new VerifyResponseTask(
|
|
||||||
plugin, packetEvent, sender, session, sharedSecret, keyPair
|
|
||||||
);
|
|
||||||
plugin.getScheduler().runAsync(verifyTask);
|
|
||||||
} else {
|
|
||||||
sender.kickPlayer(plugin.getCore().getMessage("invalid-verify-token"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean verifyNonce(Player sender, PacketContainer packet,
|
|
||||||
ClientPublicKey clientPublicKey, byte[] expectedToken) {
|
|
||||||
try {
|
|
||||||
if (new MinecraftVersion(1, 19, 0).atOrAbove()
|
|
||||||
&& !new MinecraftVersion(1, 19, 3).atOrAbove()) {
|
|
||||||
Either<byte[], ?> either = packet.getSpecificModifier(Either.class).read(0);
|
|
||||||
if (clientPublicKey == null) {
|
|
||||||
Optional<byte[]> left = either.left();
|
|
||||||
if (!left.isPresent()) {
|
|
||||||
plugin.getLog().error("No verify token sent if requested without player signed key {}", sender);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EncryptionUtil.verifyNonce(expectedToken, keyPair.getPrivate(), left.get());
|
|
||||||
} else {
|
|
||||||
Optional<?> optSignatureData = either.right();
|
|
||||||
if (!optSignatureData.isPresent()) {
|
|
||||||
plugin.getLog().error("No signature given to sent player signing key {}", sender);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object signatureData = optSignatureData.get();
|
|
||||||
long salt = FuzzyReflection.getFieldValue(signatureData, Long.TYPE, true);
|
|
||||||
byte[] signature = FuzzyReflection.getFieldValue(signatureData, byte[].class, true);
|
|
||||||
|
|
||||||
PublicKey publicKey = clientPublicKey.key();
|
|
||||||
return EncryptionUtil.verifySignedNonce(expectedToken, publicKey, salt, signature);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
byte[] nonce = packet.getByteArrays().read(1);
|
|
||||||
return EncryptionUtil.verifyNonce(expectedToken, keyPair.getPrivate(), nonce);
|
|
||||||
}
|
|
||||||
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | NoSuchPaddingException
|
|
||||||
| IllegalBlockSizeException | BadPaddingException signatureEx) {
|
|
||||||
plugin.getLog().error("Invalid signature from player {}", sender, signatureEx);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onLoginStart(PacketEvent packetEvent, Player player, String username) {
|
|
||||||
//this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
|
|
||||||
String sessionKey = player.getAddress().toString();
|
|
||||||
|
|
||||||
//remove old data every time on a new login in order to keep the session only for one person
|
|
||||||
plugin.removeSession(player.getAddress());
|
|
||||||
|
|
||||||
PacketContainer packet = packetEvent.getPacket();
|
|
||||||
Optional<ClientPublicKey> clientKey;
|
|
||||||
if (new MinecraftVersion(1, 19, 3).atOrAbove()) {
|
|
||||||
// public key sent separate
|
|
||||||
clientKey = Optional.empty();
|
|
||||||
} else {
|
|
||||||
val profileKey = packet.getOptionals(BukkitConverters.getWrappedPublicKeyDataConverter())
|
|
||||||
.optionRead(0);
|
|
||||||
|
|
||||||
clientKey = profileKey.flatMap(Function.identity()).flatMap(data -> {
|
|
||||||
Instant expires = data.getExpireTime();
|
|
||||||
PublicKey key = data.getKey();
|
|
||||||
byte[] signature = data.getSignature();
|
|
||||||
return Optional.of(ClientPublicKey.of(expires, key, signature));
|
|
||||||
});
|
|
||||||
|
|
||||||
// start reading from index 1, because 0 is already used by the public key
|
|
||||||
Optional<UUID> sessionUUID = packet.getOptionals(Converters.passthrough(UUID.class)).readSafely(1);
|
|
||||||
if (verifyClientKeys && sessionUUID.isPresent() && clientKey.isPresent()
|
|
||||||
&& verifyPublicKey(clientKey.get(), sessionUUID.get())) {
|
|
||||||
// missing or incorrect
|
|
||||||
// expired always not allowed
|
|
||||||
player.kickPlayer(plugin.getCore().getMessage("invalid-public-key"));
|
|
||||||
plugin.getLog().warn("Invalid public key from player {}", username);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
plugin.getLog().trace("GameProfile {} with {} connecting", sessionKey, username);
|
|
||||||
|
|
||||||
packetEvent.getAsyncMarker().incrementProcessingDelay();
|
|
||||||
Runnable nameCheckTask = new NameCheckTask(
|
|
||||||
plugin, random, player, packetEvent, username, clientKey.orElse(null), keyPair.getPublic()
|
|
||||||
);
|
|
||||||
plugin.getScheduler().runAsync(nameCheckTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean verifyPublicKey(ClientPublicKey clientKey, UUID sessionPremiumUUID) {
|
|
||||||
try {
|
|
||||||
return EncryptionUtil.verifyClientKey(clientKey, Instant.now(), sessionPremiumUUID);
|
|
||||||
} catch (SignatureException | InvalidKeyException | NoSuchAlgorithmException ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getUsername(PacketContainer packet) {
|
|
||||||
WrappedGameProfile profile = packet.getGameProfiles().readSafely(0);
|
|
||||||
if (profile == null) {
|
|
||||||
return packet.getStrings().read(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//player.getName() won't work at this state
|
|
||||||
return profile.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PlayerInjectionHandler getHandler() {
|
|
||||||
PacketFilterManager manager = (PacketFilterManager) ProtocolLibrary.getProtocolManager();
|
|
||||||
FieldAccessor accessor = Accessors.getFieldAccessor(manager.getClass(), PlayerInjectionHandler.class, true);
|
|
||||||
return (PlayerInjectionHandler) accessor.get(manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FloodgatePlayer getFloodgatePlayer(Player player) {
|
|
||||||
Channel channel = handler.getChannel(player);
|
|
||||||
AttributeKey<FloodgatePlayer> floodgateAttribute = AttributeKey.valueOf("floodgate-player");
|
|
||||||
return channel.attr(floodgateAttribute).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplementation of the tasks injected Floodgate in ProtocolLib that are not run due to a bug
|
|
||||||
* @see <a href="https://github.com/GeyserMC/Floodgate/issues/143">Issue Floodgate#143</a>
|
|
||||||
* @see <a href="https://github.com/GeyserMC/Floodgate/blob/5d5713ed9e9eeab0f4abdaa9cf5cd8619dc1909b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java#L121-L175">Floodgate/SpigotDataHandler</a>
|
|
||||||
* @param packetEvent the PacketEvent that won't be processed by Floodgate
|
|
||||||
* @return false if the player was kicked
|
|
||||||
*/
|
|
||||||
private boolean processFloodgateTasks(PacketEvent packetEvent) {
|
|
||||||
PacketContainer packet = packetEvent.getPacket();
|
|
||||||
Player player = packetEvent.getPlayer();
|
|
||||||
FloodgatePlayer floodgatePlayer = getFloodgatePlayer(player);
|
|
||||||
if (floodgatePlayer == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// kick the player, if necessary
|
|
||||||
Channel channel = handler.getChannel(packetEvent.getPlayer());
|
|
||||||
AttributeKey<String> kickMessageAttribute = AttributeKey.valueOf("floodgate-kick-message");
|
|
||||||
String kickMessage = channel.attr(kickMessageAttribute).get();
|
|
||||||
if (kickMessage != null) {
|
|
||||||
player.kickPlayer(kickMessage);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add prefix
|
|
||||||
String username = floodgatePlayer.getCorrectUsername();
|
|
||||||
if (packet.getGameProfiles().size() > 0) {
|
|
||||||
packet.getGameProfiles().write(0,
|
|
||||||
new WrappedGameProfile(floodgatePlayer.getCorrectUniqueId(), username));
|
|
||||||
} else {
|
|
||||||
packet.getStrings().write(0, username);
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove real Floodgate data handler
|
|
||||||
ChannelHandler floodgateHandler = channel.pipeline().get("floodgate_data_handler");
|
|
||||||
channel.pipeline().remove(floodgateHandler);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
|
||||||
|
|
||||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
|
||||||
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
|
|
||||||
import com.github.games647.craftapi.model.skin.Textures;
|
|
||||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.EventPriority;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.player.PlayerLoginEvent;
|
|
||||||
import org.bukkit.event.player.PlayerLoginEvent.Result;
|
|
||||||
|
|
||||||
public class SkinApplyListener implements Listener {
|
|
||||||
|
|
||||||
private final FastLoginBukkit plugin;
|
|
||||||
|
|
||||||
public SkinApplyListener(FastLoginBukkit plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOW)
|
|
||||||
//run this on the loginEvent to let skins plugins see the skin like in normal Minecraft behaviour
|
|
||||||
public void onPlayerLogin(PlayerLoginEvent loginEvent) {
|
|
||||||
if (loginEvent.getResult() != Result.ALLOWED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player player = loginEvent.getPlayer();
|
|
||||||
|
|
||||||
//go through every session, because player.getAddress is null
|
|
||||||
//loginEvent.getAddress is just a InetAddress not InetSocketAddress, so not unique enough
|
|
||||||
for (BukkitLoginSession session : plugin.getLoginSessions().values()) {
|
|
||||||
if (session.getUsername().equals(player.getName())) {
|
|
||||||
session.getSkin().ifPresent(skin -> applySkin(player, skin.getValue(), skin.getSignature()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applySkin(Player player, String skinData, String signature) {
|
|
||||||
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
|
|
||||||
|
|
||||||
WrappedSignedProperty skin = WrappedSignedProperty.fromValues(Textures.KEY, skinData, signature);
|
|
||||||
gameProfile.getProperties().put(Textures.KEY, skin);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,330 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
|
||||||
|
|
||||||
import com.comphenix.protocol.ProtocolLibrary;
|
|
||||||
import com.comphenix.protocol.events.PacketContainer;
|
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
|
||||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
|
||||||
import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory;
|
|
||||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
|
||||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
|
||||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
|
||||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
|
||||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
|
||||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
|
||||||
import com.comphenix.protocol.wrappers.Converters;
|
|
||||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
|
||||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
|
||||||
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
|
|
||||||
import com.github.games647.craftapi.model.auth.Verification;
|
|
||||||
import com.github.games647.craftapi.model.skin.SkinProperty;
|
|
||||||
import com.github.games647.craftapi.resolver.MojangResolver;
|
|
||||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
|
||||||
import com.github.games647.fastlogin.bukkit.InetUtils;
|
|
||||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
|
||||||
import lombok.val;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.security.Key;
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static com.comphenix.protocol.PacketType.Login.Client.START;
|
|
||||||
import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT;
|
|
||||||
|
|
||||||
public class VerifyResponseTask implements Runnable {
|
|
||||||
|
|
||||||
private static final String ENCRYPTION_CLASS_NAME = "MinecraftEncryption";
|
|
||||||
private static final Class<?> ENCRYPTION_CLASS;
|
|
||||||
private static final String ADDRESS_VERIFY_WARNING = "This indicates the use of reverse-proxy like HAProxy, "
|
|
||||||
+ "TCPShield, BungeeCord, Velocity, etc. "
|
|
||||||
+ "By default (configurable in the config) this plugin requests Mojang to verify the connecting IP "
|
|
||||||
+ "to this server with the one used to log into Minecraft to prevent MITM attacks. In "
|
|
||||||
+ "order to work this security feature, the actual client IP needs to be forwarding "
|
|
||||||
+ "(keyword IP forwarding). This process will also be useful for other server "
|
|
||||||
+ "features like IP banning, so that it doesn't ban the proxy IP.";
|
|
||||||
|
|
||||||
static {
|
|
||||||
ENCRYPTION_CLASS = MinecraftReflection.getMinecraftClass(
|
|
||||||
"util." + ENCRYPTION_CLASS_NAME, ENCRYPTION_CLASS_NAME
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final FastLoginBukkit plugin;
|
|
||||||
private final PacketEvent packetEvent;
|
|
||||||
private final KeyPair serverKey;
|
|
||||||
|
|
||||||
private final Player player;
|
|
||||||
|
|
||||||
private final BukkitLoginSession session;
|
|
||||||
|
|
||||||
private final byte[] sharedSecret;
|
|
||||||
|
|
||||||
private static Method encryptMethod;
|
|
||||||
private static Method cipherMethod;
|
|
||||||
|
|
||||||
public VerifyResponseTask(FastLoginBukkit plugin, PacketEvent packetEvent,
|
|
||||||
Player player, BukkitLoginSession session,
|
|
||||||
byte[] sharedSecret, KeyPair keyPair) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.packetEvent = packetEvent;
|
|
||||||
this.player = player;
|
|
||||||
this.session = session;
|
|
||||||
this.sharedSecret = Arrays.copyOf(sharedSecret, sharedSecret.length);
|
|
||||||
this.serverKey = keyPair;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
verifyResponse(session);
|
|
||||||
} finally {
|
|
||||||
//this is a fake packet; it shouldn't be sent to the server
|
|
||||||
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
|
|
||||||
packetEvent.setCancelled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void verifyResponse(BukkitLoginSession session) {
|
|
||||||
PrivateKey privateKey = serverKey.getPrivate();
|
|
||||||
|
|
||||||
SecretKey loginKey;
|
|
||||||
try {
|
|
||||||
loginKey = EncryptionUtil.decryptSharedKey(privateKey, sharedSecret);
|
|
||||||
} catch (GeneralSecurityException securityEx) {
|
|
||||||
disconnect("error-kick", "Cannot decrypt received contents", securityEx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!enableEncryption(loginKey)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
disconnect("error-kick", "Cannot decrypt received contents", ex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String serverId = EncryptionUtil.getServerIdHashString("", loginKey, serverKey.getPublic());
|
|
||||||
|
|
||||||
String requestedUsername = session.getRequestUsername();
|
|
||||||
InetSocketAddress socketAddress = player.getAddress();
|
|
||||||
try {
|
|
||||||
MojangResolver resolver = plugin.getCore().getResolver();
|
|
||||||
InetAddress address = socketAddress.getAddress();
|
|
||||||
Optional<Verification> response = resolver.hasJoined(requestedUsername, serverId, address);
|
|
||||||
if (response.isPresent()) {
|
|
||||||
encryptConnection(session, requestedUsername, response.get());
|
|
||||||
} else {
|
|
||||||
//user tried to fake an authentication
|
|
||||||
disconnect(
|
|
||||||
"invalid-session",
|
|
||||||
"Session server rejected incoming connection for GameProfile {} ({}). Possible reasons are"
|
|
||||||
+ "1) Client IP address contacting Mojang and server during server join were different "
|
|
||||||
+ "(Do you use a reverse proxy? -> Enable IP forwarding, "
|
|
||||||
+ "or disable the feature in the config). "
|
|
||||||
+ "2) Player is offline, but tried to bypass the authentication"
|
|
||||||
+ "3) Client uses an outdated username for connecting (Fix: Restart client)",
|
|
||||||
requestedUsername, address
|
|
||||||
);
|
|
||||||
|
|
||||||
if (InetUtils.isLocalAddress(address)) {
|
|
||||||
plugin.getLog().warn(
|
|
||||||
"The incoming request for player {} uses a local IP address",
|
|
||||||
requestedUsername
|
|
||||||
);
|
|
||||||
plugin.getLog().warn(ADDRESS_VERIFY_WARNING);
|
|
||||||
} else {
|
|
||||||
plugin.getLog().warn("If you think this is an error, please verify that the incoming "
|
|
||||||
+ "IP address {} is not associated with a server hosting company.", address);
|
|
||||||
plugin.getLog().warn(ADDRESS_VERIFY_WARNING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException ioEx) {
|
|
||||||
disconnect("error-kick", "Failed to connect to session server", ioEx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void encryptConnection(BukkitLoginSession session, String requestedUsername, Verification verification) {
|
|
||||||
plugin.getLog().info("Profile {} has a verified premium account", requestedUsername);
|
|
||||||
String realUsername = verification.getName();
|
|
||||||
if (realUsername == null) {
|
|
||||||
disconnect("invalid-session", "Username field null for {}", requestedUsername);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkinProperty[] properties = verification.getProperties();
|
|
||||||
if (properties.length > 0) {
|
|
||||||
session.setSkinProperty(properties[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
session.setVerifiedUsername(realUsername);
|
|
||||||
session.setUuid(verification.getId());
|
|
||||||
session.setVerified(true);
|
|
||||||
|
|
||||||
setPremiumUUID(session.getUuid());
|
|
||||||
receiveFakeStartPacket(realUsername, session.getClientPublicKey(), session.getUuid());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setPremiumUUID(UUID premiumUUID) {
|
|
||||||
if (plugin.getConfig().getBoolean("premiumUuid") && premiumUUID != null) {
|
|
||||||
try {
|
|
||||||
Object networkManager = getNetworkManager();
|
|
||||||
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/NetworkManager.java#L69
|
|
||||||
|
|
||||||
Class<?> managerClass = networkManager.getClass();
|
|
||||||
FieldAccessor accessor = Accessors.getFieldAccessorOrNull(managerClass, "spoofedUUID", UUID.class);
|
|
||||||
accessor.set(networkManager, premiumUUID);
|
|
||||||
} catch (Exception exc) {
|
|
||||||
plugin.getLog().error("Error setting premium uuid of {}", player, exc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//try to get the networkManager from ProtocolLib
|
|
||||||
private Object getNetworkManager() throws ClassNotFoundException {
|
|
||||||
Object injectorContainer = TemporaryPlayerFactory.getInjectorFromPlayer(player);
|
|
||||||
|
|
||||||
// ChannelInjector
|
|
||||||
Class<?> injectorClass = Class.forName("com.comphenix.protocol.injector.netty.Injector");
|
|
||||||
Object rawInjector = FuzzyReflection.getFieldValue(injectorContainer, injectorClass, true);
|
|
||||||
|
|
||||||
Class<?> rawInjectorClass = rawInjector.getClass();
|
|
||||||
FieldAccessor accessor = Accessors.getFieldAccessorOrNull(rawInjectorClass, "networkManager", Object.class);
|
|
||||||
return accessor.get(rawInjector);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean enableEncryption(SecretKey loginKey) throws IllegalArgumentException {
|
|
||||||
plugin.getLog().info("Enabling onlinemode encryption for {}", player.getAddress());
|
|
||||||
// Initialize method reflections
|
|
||||||
if (encryptMethod == null) {
|
|
||||||
Class<?> networkManagerClass = MinecraftReflection.getNetworkManagerClass();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Try to get the old (pre MC 1.16.4) encryption method
|
|
||||||
encryptMethod = FuzzyReflection.fromClass(networkManagerClass)
|
|
||||||
.getMethodByParameters("a", SecretKey.class);
|
|
||||||
} catch (IllegalArgumentException exception) {
|
|
||||||
// Get the new encryption method
|
|
||||||
encryptMethod = FuzzyReflection.fromClass(networkManagerClass)
|
|
||||||
.getMethodByParameters("a", Cipher.class, Cipher.class);
|
|
||||||
|
|
||||||
// Get the needed Cipher helper method (used to generate ciphers from login key)
|
|
||||||
cipherMethod = FuzzyReflection.fromClass(ENCRYPTION_CLASS)
|
|
||||||
.getMethodByParameters("a", int.class, Key.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Object networkManager = this.getNetworkManager();
|
|
||||||
|
|
||||||
// If cipherMethod is null - use old encryption (pre MC 1.16.4), otherwise use the new cipher one
|
|
||||||
if (cipherMethod == null) {
|
|
||||||
// Encrypt/decrypt packet flow, this behaviour is expected by the client
|
|
||||||
encryptMethod.invoke(networkManager, loginKey);
|
|
||||||
} else {
|
|
||||||
// Create ciphers from login key
|
|
||||||
Object decryptionCipher = cipherMethod.invoke(null, Cipher.DECRYPT_MODE, loginKey);
|
|
||||||
Object encryptionCipher = cipherMethod.invoke(null, Cipher.ENCRYPT_MODE, loginKey);
|
|
||||||
|
|
||||||
// Encrypt/decrypt packet flow, this behaviour is expected by the client
|
|
||||||
encryptMethod.invoke(networkManager, decryptionCipher, encryptionCipher);
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
disconnect("error-kick", "Couldn't enable encryption", ex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void disconnect(String reasonKey, String logMessage, Object... arguments) {
|
|
||||||
plugin.getLog().error(logMessage, arguments);
|
|
||||||
kickPlayer(plugin.getCore().getMessage(reasonKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void kickPlayer(String reason) {
|
|
||||||
PacketContainer kickPacket = new PacketContainer(DISCONNECT);
|
|
||||||
kickPacket.getChatComponents().write(0, WrappedChatComponent.fromText(reason));
|
|
||||||
//send kick packet at login state
|
|
||||||
//the normal event.getPlayer.kickPlayer(String) method does only work at play state
|
|
||||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, kickPacket);
|
|
||||||
//tell the server that we want to close the connection
|
|
||||||
player.kickPlayer("Disconnect");
|
|
||||||
}
|
|
||||||
|
|
||||||
//fake a new login packet in order to let the server handle all the other stuff
|
|
||||||
private void receiveFakeStartPacket(String username, ClientPublicKey clientKey, UUID uuid) {
|
|
||||||
PacketContainer startPacket;
|
|
||||||
if (new MinecraftVersion(1, 20, 2).atOrAbove()) {
|
|
||||||
startPacket = new PacketContainer(START);
|
|
||||||
startPacket.getStrings().write(0, username);
|
|
||||||
startPacket.getUUIDs().write(0, uuid);
|
|
||||||
} else if (new MinecraftVersion(1, 19, 3).atOrAbove()) {
|
|
||||||
startPacket = new PacketContainer(START);
|
|
||||||
startPacket.getStrings().write(0, username);
|
|
||||||
startPacket.getOptionals(Converters.passthrough(UUID.class)).write(0, Optional.of(uuid));
|
|
||||||
} else if (new MinecraftVersion(1, 19, 0).atOrAbove()) {
|
|
||||||
startPacket = new PacketContainer(START);
|
|
||||||
startPacket.getStrings().write(0, username);
|
|
||||||
|
|
||||||
EquivalentConverter<WrappedProfileKeyData> converter = BukkitConverters.getWrappedPublicKeyDataConverter();
|
|
||||||
val wrappedKey = Optional.ofNullable(clientKey).map(key ->
|
|
||||||
new WrappedProfileKeyData(clientKey.expiry(), clientKey.key(), clientKey.signature())
|
|
||||||
);
|
|
||||||
|
|
||||||
startPacket.getOptionals(converter).write(0, wrappedKey);
|
|
||||||
} else {
|
|
||||||
//uuid is ignored by the packet definition
|
|
||||||
WrappedGameProfile fakeProfile = new WrappedGameProfile(UUID.randomUUID(), username);
|
|
||||||
|
|
||||||
Class<?> profileHandleType = fakeProfile.getHandleType();
|
|
||||||
Class<?> packetHandleType = PacketRegistry.getPacketClassFromType(START);
|
|
||||||
ConstructorAccessor startCons = Accessors.getConstructorAccessorOrNull(packetHandleType, profileHandleType);
|
|
||||||
startPacket = new PacketContainer(START, startCons.invoke(fakeProfile.getHandle()));
|
|
||||||
}
|
|
||||||
|
|
||||||
//we don't want to handle our own packets so ignore filters
|
|
||||||
ProtocolLibrary.getProtocolManager().receiveClientPacket(player, startPacket, false);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener.protocollib.packet;
|
|
||||||
|
|
||||||
import lombok.Value;
|
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.StringJoiner;
|
|
||||||
|
|
||||||
@Accessors(fluent = true)
|
|
||||||
@Value(staticConstructor = "of")
|
|
||||||
public class ClientPublicKey {
|
|
||||||
Instant expiry;
|
|
||||||
PublicKey key;
|
|
||||||
byte[] signature;
|
|
||||||
|
|
||||||
public boolean isExpired(Instant verifyTimestamp) {
|
|
||||||
return !verifyTimestamp.isBefore(expiry);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new StringJoiner(", ", ClientPublicKey.class.getSimpleName() + '[', "]")
|
|
||||||
.add("expiry=" + expiry)
|
|
||||||
.add("key=" + Base64.getEncoder().encodeToString(key.getEncoded()))
|
|
||||||
.add("signature=" + Base64.getEncoder().encodeToString(signature))
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener.protocolsupport;
|
|
||||||
|
|
||||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
|
||||||
import protocolsupport.api.events.PlayerLoginStartEvent;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
|
|
||||||
public class ProtocolLoginSource implements LoginSource {
|
|
||||||
|
|
||||||
private final PlayerLoginStartEvent loginStartEvent;
|
|
||||||
|
|
||||||
public ProtocolLoginSource(PlayerLoginStartEvent loginStartEvent) {
|
|
||||||
this.loginStartEvent = loginStartEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void enableOnlinemode() {
|
|
||||||
loginStartEvent.setOnlineMode(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void kick(String message) {
|
|
||||||
loginStartEvent.denyLogin(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InetSocketAddress getAddress() {
|
|
||||||
return loginStartEvent.getConnection().getRawAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlayerLoginStartEvent getLoginStartEvent() {
|
|
||||||
return loginStartEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return this.getClass().getSimpleName() + '{'
|
|
||||||
+ "loginStartEvent=" + loginStartEvent
|
|
||||||
+ '}';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,139 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener.protocolsupport;
|
|
||||||
|
|
||||||
import com.github.games647.craftapi.UUIDAdapter;
|
|
||||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
|
||||||
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent;
|
|
||||||
import com.github.games647.fastlogin.core.antibot.AntiBotService;
|
|
||||||
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
|
|
||||||
import com.github.games647.fastlogin.core.shared.JoinManagement;
|
|
||||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import protocolsupport.api.events.ConnectionCloseEvent;
|
|
||||||
import protocolsupport.api.events.PlayerLoginStartEvent;
|
|
||||||
import protocolsupport.api.events.PlayerProfileCompleteEvent;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class ProtocolSupportListener extends JoinManagement<Player, CommandSender, ProtocolLoginSource>
|
|
||||||
implements Listener {
|
|
||||||
|
|
||||||
private final FastLoginBukkit plugin;
|
|
||||||
private final AntiBotService antiBotService;
|
|
||||||
|
|
||||||
public ProtocolSupportListener(FastLoginBukkit plugin, AntiBotService antiBotService) {
|
|
||||||
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
|
|
||||||
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.antiBotService = antiBotService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onLoginStart(PlayerLoginStartEvent loginStartEvent) {
|
|
||||||
if (loginStartEvent.isLoginDenied() || plugin.getCore().getAuthPluginHook() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String username = loginStartEvent.getConnection().getProfile().getName();
|
|
||||||
InetSocketAddress address = loginStartEvent.getConnection().getRawAddress();
|
|
||||||
plugin.getLog().info("Incoming login request for {} from {}", username, address);
|
|
||||||
|
|
||||||
Action action = antiBotService.onIncomingConnection(address, username);
|
|
||||||
switch (action) {
|
|
||||||
case Ignore:
|
|
||||||
// just ignore
|
|
||||||
return;
|
|
||||||
case Block:
|
|
||||||
String message = plugin.getCore().getMessage("kick-antibot");
|
|
||||||
loginStartEvent.denyLogin(message);
|
|
||||||
break;
|
|
||||||
case Continue:
|
|
||||||
default:
|
|
||||||
//remove old data every time on a new login in order to keep the session only for one person
|
|
||||||
plugin.removeSession(address);
|
|
||||||
|
|
||||||
ProtocolLoginSource source = new ProtocolLoginSource(loginStartEvent);
|
|
||||||
super.onLogin(username, source);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onConnectionClosed(ConnectionCloseEvent closeEvent) {
|
|
||||||
InetSocketAddress address = closeEvent.getConnection().getRawAddress();
|
|
||||||
plugin.removeSession(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPropertiesResolve(PlayerProfileCompleteEvent profileCompleteEvent) {
|
|
||||||
InetSocketAddress address = profileCompleteEvent.getConnection().getRawAddress();
|
|
||||||
|
|
||||||
BukkitLoginSession session = plugin.getSession(address);
|
|
||||||
|
|
||||||
if (session != null && profileCompleteEvent.getConnection().getProfile().isOnlineMode()) {
|
|
||||||
session.setVerified(true);
|
|
||||||
|
|
||||||
if (!plugin.getConfig().getBoolean("premiumUuid")) {
|
|
||||||
String username = Optional.ofNullable(profileCompleteEvent.getForcedName())
|
|
||||||
.orElse(profileCompleteEvent.getConnection().getProfile().getName());
|
|
||||||
profileCompleteEvent.setForcedUUID(UUIDAdapter.generateOfflineId(username));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FastLoginPreLoginEvent callFastLoginPreLoginEvent(String username, ProtocolLoginSource source,
|
|
||||||
StoredProfile profile) {
|
|
||||||
BukkitFastLoginPreLoginEvent event = new BukkitFastLoginPreLoginEvent(username, source, profile);
|
|
||||||
plugin.getServer().getPluginManager().callEvent(event);
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void requestPremiumLogin(ProtocolLoginSource source, StoredProfile profile, String username,
|
|
||||||
boolean registered) {
|
|
||||||
source.enableOnlinemode();
|
|
||||||
|
|
||||||
String ip = source.getAddress().getAddress().getHostAddress();
|
|
||||||
plugin.getCore().getPendingLogin().put(ip + username, new Object());
|
|
||||||
|
|
||||||
BukkitLoginSession playerSession = new BukkitLoginSession(username, registered, profile);
|
|
||||||
plugin.putSession(source.getAddress(), playerSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startCrackedSession(ProtocolLoginSource source, StoredProfile profile, String username) {
|
|
||||||
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
|
|
||||||
plugin.putSession(source.getAddress(), loginSession);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.task;
|
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
|
||||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
|
||||||
import com.github.games647.fastlogin.core.shared.FloodgateManagement;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class FloodgateAuthTask extends FloodgateManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> {
|
|
||||||
|
|
||||||
public FloodgateAuthTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player,
|
|
||||||
FloodgatePlayer floodgatePlayer) {
|
|
||||||
super(core, player, floodgatePlayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void startLogin() {
|
|
||||||
BukkitLoginSession session = new BukkitLoginSession(player.getName(), isRegistered, profile);
|
|
||||||
|
|
||||||
// enable auto login based on the value of 'autoLoginFloodgate' in config.yml
|
|
||||||
session.setVerified(isAutoAuthAllowed(autoLoginFloodgate));
|
|
||||||
|
|
||||||
// run login task
|
|
||||||
Runnable forceLoginTask = new ForceLoginTask(core.getPlugin().getCore(), player, session);
|
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(core.getPlugin(), forceLoginTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getName(Player player) {
|
|
||||||
return player.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected UUID getUUID(Player player) {
|
|
||||||
return player.getUniqueId();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected InetSocketAddress getAddress(Player player) {
|
|
||||||
return player.getAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
# project data for Bukkit in order to register our plugin with all it's components
|
# project data for Bukkit in order to register our plugin with all it components
|
||||||
# ${-} are variables from Maven (pom.xml) which will be replaced after the build
|
# ${-} are variables from Maven (pom.xml) which will be replaced after the build
|
||||||
name: ${project.parent.name}
|
name: ${project.parent.name}
|
||||||
version: ${project.version}-${git.commit.id.abbrev}
|
version: ${project.version}-${git.commit.id.abbrev}
|
||||||
@ -11,22 +11,18 @@ description: |
|
|||||||
website: ${project.url}
|
website: ${project.url}
|
||||||
dev-url: ${project.url}
|
dev-url: ${project.url}
|
||||||
|
|
||||||
# This plugin doesn't have to be transformed for compatibility with Minecraft >= 1.13
|
# This plugin don't have to be transformed for compatibility with Minecraft >= 1.13
|
||||||
api-version: '1.13'
|
api-version: '1.13'
|
||||||
|
|
||||||
softdepend:
|
softdepend:
|
||||||
# We depend on either ProtocolLib or ProtocolSupport
|
# We depend either ProtocolLib or ProtocolSupport
|
||||||
- ProtocolSupport
|
- ProtocolSupport
|
||||||
- ProtocolLib
|
- ProtocolLib
|
||||||
# Premium variable
|
# Premium variable
|
||||||
- PlaceholderAPI
|
- PlaceholderAPI
|
||||||
# Bedrock Player Bridge
|
|
||||||
- Geyser-Spigot
|
|
||||||
- floodgate
|
|
||||||
# Auth plugins
|
# Auth plugins
|
||||||
- AuthMe
|
- AuthMe
|
||||||
- LoginSecurity
|
- LoginSecurity
|
||||||
- SodionAuth
|
|
||||||
- xAuth
|
- xAuth
|
||||||
- LogIt
|
- LogIt
|
||||||
- UltraAuth
|
- UltraAuth
|
||||||
|
Binary file not shown.
@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit;
|
|
||||||
|
|
||||||
import com.github.games647.fastlogin.core.CommonUtil;
|
|
||||||
import lombok.val;
|
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
|
||||||
import net.md_5.bungee.chat.ComponentSerializer;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
class FastLoginBukkitTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testRGB() {
|
|
||||||
val message = "&x00002a00002b&lText";
|
|
||||||
val msg = CommonUtil.translateColorCodes(message);
|
|
||||||
assertEquals(msg, "§x00002a00002b§lText");
|
|
||||||
|
|
||||||
val components = TextComponent.fromLegacyText(msg);
|
|
||||||
val expected = "{\"bold\":true,\"color\":\"#00a00b\",\"text\":\"Text\"}";
|
|
||||||
assertEquals(ComponentSerializer.toString(components), expected);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit;
|
|
||||||
|
|
||||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
|
||||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
|
||||||
import fr.xephi.authme.api.v3.AuthMeApi;
|
|
||||||
import fr.xephi.authme.process.Management;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
|
|
||||||
class ReflectionTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testAuthMeManagementField() {
|
|
||||||
FieldAccessor accessor = Accessors.getFieldAccessor(AuthMeApi.class, Management.class, true);
|
|
||||||
assertNotNull(accessor.getField());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.hook;
|
|
||||||
|
|
||||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
|
||||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
|
||||||
import de.st_ddt.crazylogin.CrazyLogin;
|
|
||||||
import de.st_ddt.crazylogin.listener.PlayerListener;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
|
|
||||||
class CrazyLoginHookTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testPlayerListener() {
|
|
||||||
FieldAccessor accessor = Accessors.getFieldAccessor(CrazyLogin.class, PlayerListener.class, true);
|
|
||||||
assertNotNull(accessor.getField());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
|
||||||
|
|
||||||
import com.google.gson.TypeAdapter;
|
|
||||||
import com.google.gson.stream.JsonReader;
|
|
||||||
import com.google.gson.stream.JsonWriter;
|
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
public class Base64Adapter extends TypeAdapter<byte[]> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(JsonWriter out, byte[] value) throws IOException {
|
|
||||||
val encoded = Base64.getEncoder().encodeToString(value);
|
|
||||||
out.value(encoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] read(JsonReader in) throws IOException {
|
|
||||||
String encoded = in.nextString();
|
|
||||||
return Base64.getDecoder().decode(encoded);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,298 +1,43 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.SignatureTestData.SignatureData;
|
import com.github.games647.fastlogin.bukkit.auth.protocollib.EncryptionUtil;
|
||||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
|
||||||
import com.google.common.hash.Hashing;
|
|
||||||
import lombok.val;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
import java.security.SecureRandom;
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.Key;
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.security.interfaces.RSAPublicKey;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import org.junit.Test;
|
||||||
|
|
||||||
class EncryptionUtilTest {
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
|
||||||
|
public class EncryptionUtilTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testVerifyToken() {
|
public void testVerifyToken() throws Exception {
|
||||||
val random = ThreadLocalRandom.current();
|
SecureRandom random = new SecureRandom();
|
||||||
byte[] token = EncryptionUtil.generateVerifyToken(random);
|
byte[] token = EncryptionUtil.generateVerifyToken(random);
|
||||||
|
|
||||||
assertAll(
|
assertThat(token, notNullValue());
|
||||||
() -> assertNotNull(token),
|
assertThat(token.length, is(4));
|
||||||
() -> assertEquals(token.length, 4)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
// @Test
|
||||||
void testServerKey() {
|
// public void testDecryptSharedSecret() throws Exception {
|
||||||
KeyPair keyPair = EncryptionUtil.generateKeyPair();
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// public void testDecryptData() throws Exception {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
Key privateKey = keyPair.getPrivate();
|
// private static SecretKey createNewSharedKey() {
|
||||||
assertEquals(privateKey.getAlgorithm(), "RSA");
|
// try {
|
||||||
|
// KeyGenerator keygenerator = KeyGenerator.getInstance("AES");
|
||||||
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
// keygenerator.init(128);
|
||||||
assertEquals(publicKey.getAlgorithm(), "RSA");
|
// return keygenerator.generateKey();
|
||||||
|
// } catch (NoSuchAlgorithmException nosuchalgorithmexception) {
|
||||||
// clients accept larger values than the standard vanilla server, but we shouldn't crash them
|
// throw new Error(nosuchalgorithmexception);
|
||||||
assertAll(
|
// }
|
||||||
() -> assertTrue(publicKey.getModulus().bitLength() >= 1024),
|
// }
|
||||||
() -> assertTrue(publicKey.getModulus().bitLength() < 8192)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testExpiredClientKey() throws Exception {
|
|
||||||
val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
|
|
||||||
|
|
||||||
// Client expires at the exact second mentioned, so use it for verification
|
|
||||||
val expiredTimestamp = clientKey.expiry();
|
|
||||||
assertFalse(EncryptionUtil.verifyClientKey(clientKey, expiredTimestamp, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@ValueSource(strings = {
|
|
||||||
// expiration date changed should make the signature invalid while still being not expired
|
|
||||||
"client_keys/invalid_wrong_expiration.json",
|
|
||||||
// changed public key no longer corresponding to the signature
|
|
||||||
"client_keys/invalid_wrong_key.json",
|
|
||||||
// signature modified no longer corresponding to key and expiration date
|
|
||||||
"client_keys/invalid_wrong_signature.json"
|
|
||||||
})
|
|
||||||
void testInvalidClientKey(String clientKeySource) throws Exception {
|
|
||||||
val clientKey = ResourceLoader.loadClientKey(clientKeySource);
|
|
||||||
Instant expireTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
|
|
||||||
|
|
||||||
assertFalse(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testValidClientKey() throws Exception {
|
|
||||||
val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
|
|
||||||
val verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
|
|
||||||
|
|
||||||
assertTrue(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testValid191ClientKey() throws Exception {
|
|
||||||
val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key_19_1.json");
|
|
||||||
val verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
|
|
||||||
|
|
||||||
val ownerPremiumId = UUID.fromString("0aaa2c13-922a-411b-b655-9b8c08404695");
|
|
||||||
assertTrue(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp, ownerPremiumId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testIncorrect191ClientOwner() throws Exception {
|
|
||||||
val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key_19_1.json");
|
|
||||||
val verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
|
|
||||||
|
|
||||||
val ownerPremiumId = UUID.fromString("61699b2e-d327-4a01-9f1e-0ea8c3f06bc6");
|
|
||||||
assertFalse(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp, ownerPremiumId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testDecryptSharedSecret() throws Exception {
|
|
||||||
KeyPair serverPair = EncryptionUtil.generateKeyPair();
|
|
||||||
val serverPK = serverPair.getPublic();
|
|
||||||
|
|
||||||
SecretKey secretKey = generateSharedKey();
|
|
||||||
byte[] encryptedSecret = encrypt(serverPK, secretKey.getEncoded());
|
|
||||||
|
|
||||||
SecretKey decryptSharedKey = EncryptionUtil.decryptSharedKey(serverPair.getPrivate(), encryptedSecret);
|
|
||||||
assertEquals(decryptSharedKey, secretKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] encrypt(PublicKey receiverKey, byte... message)
|
|
||||||
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
|
|
||||||
IllegalBlockSizeException, BadPaddingException {
|
|
||||||
val encryptCipher = Cipher.getInstance(receiverKey.getAlgorithm());
|
|
||||||
encryptCipher.init(Cipher.ENCRYPT_MODE, receiverKey);
|
|
||||||
return encryptCipher.doFinal(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SecretKeySpec generateSharedKey() {
|
|
||||||
// according to wiki.vg 16 bytes long
|
|
||||||
byte[] sharedKey = new byte[16];
|
|
||||||
ThreadLocalRandom.current().nextBytes(sharedKey);
|
|
||||||
// shared key is to be used for the AES/CFB8 stream cipher to encrypt the traffic
|
|
||||||
// therefore the encryption/decryption has to be AES
|
|
||||||
return new SecretKeySpec(sharedKey, "AES");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testServerIdHash() throws Exception {
|
|
||||||
val serverId = "";
|
|
||||||
val sharedSecret = generateSharedKey();
|
|
||||||
val serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
|
|
||||||
|
|
||||||
String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
|
|
||||||
assertEquals(EncryptionUtil.getServerIdHashString(serverId, sharedSecret, serverPK), sessionHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getServerHash(@SuppressWarnings("SameParameterValue") CharSequence serverId,
|
|
||||||
SecretKey sharedSecret, PublicKey serverPK) {
|
|
||||||
// https://wiki.vg/Protocol_Encryption#Client
|
|
||||||
// sha1 := Sha1()
|
|
||||||
// sha1.update(ASCII encoding of the server id string from Encryption Request)
|
|
||||||
// sha1.update(shared secret)
|
|
||||||
// sha1.update(server's encoded public key from Encryption Request)
|
|
||||||
// hash := sha1.hexdigest() # String of hex characters
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
val hasher = Hashing.sha1().newHasher();
|
|
||||||
hasher.putString(serverId, StandardCharsets.US_ASCII);
|
|
||||||
hasher.putBytes(sharedSecret.getEncoded());
|
|
||||||
hasher.putBytes(serverPK.getEncoded());
|
|
||||||
// It works by treating the sha1 output bytes as one large integer in two's complement and then printing the
|
|
||||||
// integer in base 16, placing a minus sign if the interpreted number is negative.
|
|
||||||
// reference:
|
|
||||||
// https://github.com/SpigotMC/BungeeCord/blob/ff5727c5ef9c0b56ad35f9816ae6bd660b622cf0/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java#L456
|
|
||||||
return new BigInteger(hasher.hash().asBytes()).toString(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testServerIdHashWrongSecret() throws Exception {
|
|
||||||
val serverId = "";
|
|
||||||
val sharedSecret = generateSharedKey();
|
|
||||||
val serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
|
|
||||||
|
|
||||||
String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
|
|
||||||
assertNotEquals(EncryptionUtil.getServerIdHashString("", generateSharedKey(), serverPK), sessionHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testServerIdHashWrongServerKey() {
|
|
||||||
val serverId = "";
|
|
||||||
val sharedSecret = generateSharedKey();
|
|
||||||
val serverPK = EncryptionUtil.generateKeyPair().getPublic();
|
|
||||||
|
|
||||||
String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
|
|
||||||
val wrongPK = EncryptionUtil.generateKeyPair().getPublic();
|
|
||||||
assertNotEquals(EncryptionUtil.getServerIdHashString("", sharedSecret, wrongPK), sessionHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testValidSignedNonce() throws Exception {
|
|
||||||
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
|
|
||||||
SignatureTestData testData = SignatureTestData.fromResource("signature/valid_signature.json");
|
|
||||||
assertTrue(verifySignedNonce(testData, clientKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@ValueSource(strings = {
|
|
||||||
"signature/incorrect_nonce.json",
|
|
||||||
"signature/incorrect_salt.json",
|
|
||||||
"signature/incorrect_signature.json",
|
|
||||||
})
|
|
||||||
void testIncorrectNonce(String signatureSource) throws Exception {
|
|
||||||
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
|
|
||||||
SignatureTestData testData = SignatureTestData.fromResource(signatureSource);
|
|
||||||
assertFalse(verifySignedNonce(testData, clientKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testWrongPublicKeySigned() throws Exception {
|
|
||||||
// load a different public key
|
|
||||||
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/invalid_wrong_key.json");
|
|
||||||
SignatureTestData testData = SignatureTestData.fromResource("signature/valid_signature.json");
|
|
||||||
assertFalse(verifySignedNonce(testData, clientKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean verifySignedNonce(SignatureTestData testData, ClientPublicKey clientKey)
|
|
||||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
|
||||||
PublicKey clientPublicKey = clientKey.key();
|
|
||||||
|
|
||||||
byte[] nonce = testData.getNonce();
|
|
||||||
SignatureData signature = testData.getSignature();
|
|
||||||
long salt = signature.getSalt();
|
|
||||||
return EncryptionUtil.verifySignedNonce(nonce, clientPublicKey, salt, signature.getSignature());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testNonce() throws Exception {
|
|
||||||
byte[] expected = {1, 2, 3, 4};
|
|
||||||
val serverKey = EncryptionUtil.generateKeyPair();
|
|
||||||
val encryptedNonce = encrypt(serverKey.getPublic(), expected);
|
|
||||||
|
|
||||||
assertTrue(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testNonceIncorrect() throws Exception {
|
|
||||||
byte[] expected = {1, 2, 3, 4};
|
|
||||||
val serverKey = EncryptionUtil.generateKeyPair();
|
|
||||||
|
|
||||||
// flipped first character
|
|
||||||
val encryptedNonce = encrypt(serverKey.getPublic(), new byte[]{0, 2, 3, 4});
|
|
||||||
assertFalse(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testNonceFailedDecryption() throws Exception {
|
|
||||||
byte[] expected = {1, 2, 3, 4};
|
|
||||||
val serverKey = EncryptionUtil.generateKeyPair();
|
|
||||||
// generate a new keypair that is different
|
|
||||||
val encryptedNonce = encrypt(EncryptionUtil.generateKeyPair().getPublic(), expected);
|
|
||||||
|
|
||||||
assertThrows(GeneralSecurityException.class,
|
|
||||||
() -> EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testNonceIncorrectEmpty() {
|
|
||||||
byte[] expected = {1, 2, 3, 4};
|
|
||||||
val serverKey = EncryptionUtil.generateKeyPair();
|
|
||||||
byte[] encryptedNonce = {};
|
|
||||||
|
|
||||||
assertThrows(GeneralSecurityException.class,
|
|
||||||
() -> EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
|
||||||
import com.google.common.io.Resources;
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import org.bouncycastle.util.io.pem.PemObject;
|
|
||||||
import org.bouncycastle.util.io.pem.PemReader;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.KeyFactory;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.interfaces.RSAPrivateKey;
|
|
||||||
import java.security.interfaces.RSAPublicKey;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
|
||||||
import java.security.spec.KeySpec;
|
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
|
||||||
import java.security.spec.X509EncodedKeySpec;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
public class ResourceLoader {
|
|
||||||
|
|
||||||
public static RSAPrivateKey parsePrivateKey(String keySpec)
|
|
||||||
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
|
|
||||||
try (
|
|
||||||
Reader reader = new StringReader(keySpec);
|
|
||||||
PemReader pemReader = new PemReader(reader)
|
|
||||||
) {
|
|
||||||
PemObject pemObject = pemReader.readPemObject();
|
|
||||||
byte[] content = pemObject.getContent();
|
|
||||||
KeySpec privateKeySpec = new PKCS8EncodedKeySpec(content);
|
|
||||||
|
|
||||||
KeyFactory factory = KeyFactory.getInstance("RSA");
|
|
||||||
return (RSAPrivateKey) factory.generatePrivate(privateKeySpec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static ClientPublicKey loadClientKey(String path)
|
|
||||||
throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {
|
|
||||||
URL keyUrl = Resources.getResource(path);
|
|
||||||
|
|
||||||
String lines = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
|
|
||||||
JsonObject object = new Gson().fromJson(lines, JsonObject.class);
|
|
||||||
|
|
||||||
Instant expires = Instant.parse(object.getAsJsonPrimitive("expires_at").getAsString());
|
|
||||||
String key = object.getAsJsonPrimitive("key").getAsString();
|
|
||||||
RSAPublicKey publicKey = parsePublicKey(key);
|
|
||||||
|
|
||||||
byte[] signature = Base64.getDecoder().decode(object.getAsJsonPrimitive("signature").getAsString());
|
|
||||||
return ClientPublicKey.of(expires, publicKey, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RSAPublicKey parsePublicKey(String keySpec)
|
|
||||||
throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
|
|
||||||
try (
|
|
||||||
Reader reader = new StringReader(keySpec);
|
|
||||||
PemReader pemReader = new PemReader(reader)
|
|
||||||
) {
|
|
||||||
PemObject pemObject = pemReader.readPemObject();
|
|
||||||
byte[] content = pemObject.getContent();
|
|
||||||
KeySpec pubKeySpec = new X509EncodedKeySpec(content);
|
|
||||||
|
|
||||||
KeyFactory factory = KeyFactory.getInstance("RSA");
|
|
||||||
return (RSAPublicKey) factory.generatePublic(pubKeySpec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
|
||||||
|
|
||||||
import com.google.common.io.Resources;
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.annotations.JsonAdapter;
|
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
public class SignatureTestData {
|
|
||||||
|
|
||||||
public static SignatureTestData fromResource(String resourceName) throws IOException {
|
|
||||||
val keyUrl = Resources.getResource(resourceName);
|
|
||||||
val encodedSignature = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
|
|
||||||
|
|
||||||
return new Gson().fromJson(encodedSignature, SignatureTestData.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonAdapter(Base64Adapter.class)
|
|
||||||
private byte[] nonce;
|
|
||||||
|
|
||||||
private SignatureData signature;
|
|
||||||
|
|
||||||
public byte[] getNonce() {
|
|
||||||
return nonce;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SignatureData getSignature() {
|
|
||||||
return signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SignatureData {
|
|
||||||
|
|
||||||
private long salt;
|
|
||||||
|
|
||||||
@JsonAdapter(Base64Adapter.class)
|
|
||||||
private byte[] signature;
|
|
||||||
|
|
||||||
public long getSalt() {
|
|
||||||
return salt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getSignature() {
|
|
||||||
return signature;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
|
||||||
|
|
||||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
|
||||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
|
||||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.mockito.MockedStatic;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.Mockito.mockStatic;
|
|
||||||
|
|
||||||
class VerifyResponseTaskTest {
|
|
||||||
|
|
||||||
private static final String NETTY_INJECTOR_CLASS =
|
|
||||||
"com.comphenix.protocol.injector.netty.channel.NettyChannelInjector";
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getNetworkManagerReflection() throws ClassNotFoundException {
|
|
||||||
try (
|
|
||||||
MockedStatic<Bukkit> bukkitMock = mockStatic(Bukkit.class);
|
|
||||||
MockedStatic<MinecraftReflection> reflectionMock = mockStatic(MinecraftReflection.class);
|
|
||||||
MockedStatic<PacketRegistry> registryMock = mockStatic(PacketRegistry.class)
|
|
||||||
) {
|
|
||||||
bukkitMock.when(Bukkit::getVersion).thenReturn("git-Bukkit-18fbb24 (MC: 1.17)");
|
|
||||||
reflectionMock.when(MinecraftReflection::getMinecraftPackage).thenReturn("xyz");
|
|
||||||
reflectionMock.when(MinecraftReflection::getEnumProtocolClass).thenReturn(Object.class);
|
|
||||||
|
|
||||||
registryMock.when(() -> PacketRegistry.tryGetPacketClass(any())).thenReturn(Optional.empty());
|
|
||||||
|
|
||||||
|
|
||||||
Class<?> injectorClass = Class.forName(NETTY_INJECTOR_CLASS);
|
|
||||||
FieldAccessor accessor = Accessors.getFieldAccessorOrNull(injectorClass, "networkManager", Object.class);
|
|
||||||
assertNotNull(accessor.getField());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bukkit.task;
|
|
||||||
|
|
||||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
|
||||||
import lombok.val;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
|
|
||||||
class DelayedAuthHookTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void createNewReflectiveInstance() throws ReflectiveOperationException {
|
|
||||||
val authHook = new DelayedAuthHook(null);
|
|
||||||
assertNotNull(authHook.newInstance(DummyHook.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DummyHook implements AuthPlugin<Player> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean forceLogin(Player player) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean forceRegister(Player player, String password) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRegistered(String playerName) throws Exception {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
# Integration tests for authentication
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
Projects require integration tests in order to check against errors that could only occur if connected to other
|
|
||||||
components. However, they are heavier in terms of performance and require a more complex setup. Unit tests often make
|
|
||||||
use of fake, mock, stubs, etc. implementations to test the unit in isolation and thus could hide issues across
|
|
||||||
boundaries of a unit. Nevertheless, both are not replacement for each other.
|
|
||||||
|
|
||||||
## Usage in this project
|
|
||||||
|
|
||||||
The authentication system is a core component, so it requires some kind of testing. Here we are going to
|
|
||||||
spin up a Spigot server and test with the supported authentication schemes against the implementation of MCProtocolLib.
|
|
||||||
|
|
||||||
### Goals
|
|
||||||
|
|
||||||
* OS platform independent
|
|
||||||
* Reproducible, but not fixed to a specific image hash
|
|
||||||
* This is a dev container, so fixing it to feature/major version is enough instead of a version fixed by hash
|
|
||||||
* Improve container spin up
|
|
||||||
* E.g. Remove/Reduce world generation
|
|
||||||
|
|
||||||
### Note on automation
|
|
||||||
|
|
||||||
The simplest solution it to use the official Mojang session and authentication servers. However, this would require
|
|
||||||
a spare Minecraft account. Mocking the auth servers would be a solution to avoid this.
|
|
||||||
|
|
||||||
## Related
|
|
||||||
|
|
||||||
Interest blog article about integration tests and why they are necessary.
|
|
||||||
https://software.rajivprab.com/2019/04/28/rethinking-software-testing-perspectives-from-the-world-of-hardware/
|
|
||||||
|
|
||||||
## Issues
|
|
||||||
|
|
||||||
### Slow startup
|
|
||||||
|
|
||||||
Tried a lot of optimizations like only loading a single world without the nether or the end. However, there the startup
|
|
||||||
is still slow. If you have any ideas on how to tune the startup parameters of the Minecraft server or the JVM
|
|
||||||
itself to reduce the startup time, please suggest it.
|
|
||||||
|
|
||||||
### Checkpoint
|
|
||||||
|
|
||||||
An idea to optimize the time is to use CRIU (checkpoint and restore). So to save the process at a certain stage and
|
|
||||||
restore all data multiple times. This could cause a lot of issues like open files have to be present. However, the
|
|
||||||
impact is significant and since it runs inside the container all files, pids (pid=1) should be matching. Potential
|
|
||||||
checkpoint locations are:
|
|
||||||
|
|
||||||
* Direct before loading the plugins
|
|
||||||
* Likely before binding the port to prevent issues
|
|
||||||
* After loading the libraries
|
|
||||||
|
|
||||||
Nevertheless, the current state requires to run it with root and the Java support is currently still in progress.
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
|||||||
# About
|
|
||||||
|
|
||||||
This contains test resources for the unit tests. The files are extracted from the Minecraft directory with slight
|
|
||||||
modifications. The files are found in `$MINECRAFT_HOME$/profilekeys/`, where `$MINECRAFT_HOME$` represents the
|
|
||||||
OS-dependent minecraft folder.
|
|
||||||
|
|
||||||
**Notable the files in this folder do not contain the private key information. It should be explicitly
|
|
||||||
stripped before including it.**
|
|
||||||
|
|
||||||
## Minecraft folder
|
|
||||||
|
|
||||||
* Windows: `%appdata%\.minecraft`
|
|
||||||
* Linux: `/home/username/.minecraft`
|
|
||||||
* Mac: `~/Library/Application Support/minecraft`
|
|
||||||
|
|
||||||
## Directory structure
|
|
||||||
|
|
||||||
* `valid_public_key.json`: Extracted from the actual file
|
|
||||||
* `invalid_wrong_expiration.json`: Changed the expiration date
|
|
||||||
* `invalid_wrong_key.json`: Modified public key while keeping the RSA structure valid
|
|
||||||
* `invalid_wrong_signature.json`: Changed a character in the public key signature
|
|
||||||
|
|
||||||
## File content
|
|
||||||
|
|
||||||
* `expires_at`: Expiration date
|
|
||||||
* `key`: Public key from the original file out of `public_key.key`
|
|
||||||
* `signature`: Mojang signed signature of this public key
|
|
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"expires_at": "2022-06-20T07:31:47.318722344Z",
|
|
||||||
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd3ZxDhcRWWru1XEBke6uYqmbnS2Oxyk\nOMj+QDKrkwUqhVJYciyXGsMx46Mgr/KIoGCcokP5OtIxc6+69/ZLqJ9PvM81kLIxAqyvfBMKMGjP\n376LgxTF1FeDpbe5zXaNRxfmnvQhS5YTLbzgk36qWVjqxJMG4VLVmh7RV5zWsb7XlckZb2zRHM2Y\nMHbEC+ggX+l6zQyfG1KK0MH5k+O6b0xD0rv1wm24sLOesTXH6RZG8cNE3ofdnavxjFodTOnra6w1\naiVcoUTdEPSS86wQwq9j0YCcAKOwMXsqbk9NhpujrdyJ94dev+ELwkNS7P0pPrcfiyFTQeJCZTXz\nJB36MwIDAQAB\n-----END RSA PUBLIC KEY-----\n",
|
|
||||||
"signature": "lfRXK4zL213wBKg760eiPV7yvnLZ6a6v9Iohmw78yxIzqXO3tfrC5Z+P2LGiO1BdI4xckx8yz4ktn82zX97+r2zktBw0As7g71H/FjInpoZ76j3gMUaiFNrQJ0vKCCI7xsjonemroWAVDCAqlvdyqwUu/Fnz85+WoR2kCQ721vwy6IjWA3xhq8XrWjkI/AlBmoS/kVqnvjjjc9vocdddJXbUYzCse/hWWIbsFeBXyiGCd3v7apgtXwQfM++tt87fq7444zQskiYb14oQP8/uNwqZWQ9jAs00i1BZ0MNM6+TZYGHOfS6rbHZ1bcX34VZdcCwpapK/Z2HBRIgDN4QOcgJkyq1GcjvlM2wjfhN8gXTsmbF9Ee+5Y6a4ONRkxRZK2sT8oAXdm0OlTEGB0P0+WRRFOQ/PnRqbI7lvANao2METT2EUHHrtqFMe53kqCHdzy5qyuHxdCEa6l/gSR08fybx9DdRRmhOlhSPGxhgwqyi1fEMrN4CsSKNrv5u+sMqhspA05b3DQJeLDX+UV5ujRHwm0A49NF+h1ZYlrcefz5IMUUisOOw6HiLc/YGLD2jHwSePGdfMwMnrIxbxjCta7/7A91aaN7eYm16KW9erCOwAfJmBSQC6Pbmg5f7rd7rAKVOPxglq7nayXmd+BK53Mal5tltMSgd/0iY6SEtGSEU="
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"expires_at": "2022-06-20T08:31:47.318722344Z",
|
|
||||||
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoOv23jt2QPyab6bPRBwH2ggmzQU3I+xmDpi3X5ZB5Em/4uzyZqNVLJc0gShpk0XsdoB28Nq1bPxczOTBxuXi3rg5ax5gL+iymDSU27DLM8s/33lOofzGPJUEQGKlFm0QelDKZ/q5Y/9inHE3hEJKf7h0tnmGahXFmZSF/nRz9GHnfSYpjtDr9bsZOzQuLhHXT5E4ksNRTFW41h0MlZ1qOhO+NiiVgk7LmgVYiV7RRbgO8U6RaCEqg5n28Ewo6QtzB+DF4NTDeu3E9BLH5G0npdUrVNhdRUWCFDmH6n9hqSIz2J7o6GvWqEvp0h9e/3qtLsoS60hnQXunrcWcPaEIYQIDAQAB\n-----END RSA PUBLIC KEY-----\n",
|
|
||||||
"signature": "lfRXK4zL213wBKg760eiPV7yvnLZ6a6v9Iohmw78yxIzqXO3tfrC5Z+P2LGiO1BdI4xckx8yz4ktn82zX97+r2zktBw0As7g71H/FjInpoZ76j3gMUaiFNrQJ0vKCCI7xsjonemroWAVDCAqlvdyqwUu/Fnz85+WoR2kCQ721vwy6IjWA3xhq8XrWjkI/AlBmoS/kVqnvjjjc9vocdddJXbUYzCse/hWWIbsFeBXyiGCd3v7apgtXwQfM++tt87fq7444zQskiYb14oQP8/uNwqZWQ9jAs00i1BZ0MNM6+TZYGHOfS6rbHZ1bcX34VZdcCwpapK/Z2HBRIgDN4QOcgJkyq1GcjvlM2wjfhN8gXTsmbF9Ee+5Y6a4ONRkxRZK2sT8oAXdm0OlTEGB0P0+WRRFOQ/PnRqbI7lvANao2METT2EUHHrtqFMe53kqCHdzy5qyuHxdCEa6l/gSR08fybx9DdRRmhOlhSPGxhgwqyi1fEMrN4CsSKNrv5u+sMqhspA05b3DQJeLDX+UV5ujRHwm0A49NF+h1ZYlrcefz5IMUUisOOw6HiLc/YGLD2jHwSePGdfMwMnrIxbxjCta7/7A91aaN7eYm16KW9erCOwAfJmBSQC6Pbmg5f7rd7rAKVOPxglq7nayXmd+BK53Mal5tltMSgd/0iY6SEtGSEU="
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"expires_at": "2022-06-20T08:31:47.318722344Z",
|
|
||||||
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd3ZxDhcRWWru1XEBke6uYqmbnS2Oxyk\nOMj+QDKrkwUqhVJYciyXGsMx46Mgr/KIoGCcokP5OtIxc6+69/ZLqJ9PvM81kLIxAqyvfBMKMGjP\n376LgxTF1FeDpbe5zXaNRxfmnvQhS5YTLbzgk36qWVjqxJMG4VLVmh7RV5zWsb7XlckZb2zRHM2Y\nMHbEC+ggX+l6zQyfG1KK0MH5k+O6b0xD0rv1wm24sLOesTXH6RZG8cNE3ofdnavxjFodTOnra6w1\naiVcoUTdEPSS86wQwq9j0YCcAKOwMXsqbk9NhpujrdyJ94dev+ELwkNS7P0pPrcfiyFTQeJCZTXz\nJB36MwIDAQAB\n-----END RSA PUBLIC KEY-----\n",
|
|
||||||
"signature": "lfRxK4zL213wBKg760eiPV7yvnLZ6a6v9Iohmw78yxIzqXO3tfrC5Z+P2LGiO1BdI4xckx8yz4ktn82zX97+r2zktBw0As7g71H/FjInpoZ76j3gMUaiFNrQJ0vKCCI7xsjonemroWAVDCAqlvdyqwUu/Fnz85+WoR2kCQ721vwy6IjWA3xhq8XrWjkI/AlBmoS/kVqnvjjjc9vocdddJXbUYzCse/hWWIbsFeBXyiGCd3v7apgtXwQfM++tt87fq7444zQskiYb14oQP8/uNwqZWQ9jAs00i1BZ0MNM6+TZYGHOfS6rbHZ1bcX34VZdcCwpapK/Z2HBRIgDN4QOcgJkyq1GcjvlM2wjfhN8gXTsmbF9Ee+5Y6a4ONRkxRZK2sT8oAXdm0OlTEGB0P0+WRRFOQ/PnRqbI7lvANao2METT2EUHHrtqFMe53kqCHdzy5qyuHxdCEa6l/gSR08fybx9DdRRmhOlhSPGxhgwqyi1fEMrN4CsSKNrv5u+sMqhspA05b3DQJeLDX+UV5ujRHwm0A49NF+h1ZYlrcefz5IMUUisOOw6HiLc/YGLD2jHwSePGdfMwMnrIxbxjCta7/7A91aaN7eYm16KW9erCOwAfJmBSQC6Pbmg5f7rd7rAKVOPxglq7nayXmd+BK53Mal5tltMSgd/0iY6SEtGSEU="
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"expires_at": "2022-06-20T08:31:47.318722344Z",
|
|
||||||
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd3ZxDhcRWWru1XEBke6uYqmbnS2Oxyk\nOMj+QDKrkwUqhVJYciyXGsMx46Mgr/KIoGCcokP5OtIxc6+69/ZLqJ9PvM81kLIxAqyvfBMKMGjP\n376LgxTF1FeDpbe5zXaNRxfmnvQhS5YTLbzgk36qWVjqxJMG4VLVmh7RV5zWsb7XlckZb2zRHM2Y\nMHbEC+ggX+l6zQyfG1KK0MH5k+O6b0xD0rv1wm24sLOesTXH6RZG8cNE3ofdnavxjFodTOnra6w1\naiVcoUTdEPSS86wQwq9j0YCcAKOwMXsqbk9NhpujrdyJ94dev+ELwkNS7P0pPrcfiyFTQeJCZTXz\nJB36MwIDAQAB\n-----END RSA PUBLIC KEY-----\n",
|
|
||||||
"signature": "lfRXK4zL213wBKg760eiPV7yvnLZ6a6v9Iohmw78yxIzqXO3tfrC5Z+P2LGiO1BdI4xckx8yz4ktn82zX97+r2zktBw0As7g71H/FjInpoZ76j3gMUaiFNrQJ0vKCCI7xsjonemroWAVDCAqlvdyqwUu/Fnz85+WoR2kCQ721vwy6IjWA3xhq8XrWjkI/AlBmoS/kVqnvjjjc9vocdddJXbUYzCse/hWWIbsFeBXyiGCd3v7apgtXwQfM++tt87fq7444zQskiYb14oQP8/uNwqZWQ9jAs00i1BZ0MNM6+TZYGHOfS6rbHZ1bcX34VZdcCwpapK/Z2HBRIgDN4QOcgJkyq1GcjvlM2wjfhN8gXTsmbF9Ee+5Y6a4ONRkxRZK2sT8oAXdm0OlTEGB0P0+WRRFOQ/PnRqbI7lvANao2METT2EUHHrtqFMe53kqCHdzy5qyuHxdCEa6l/gSR08fybx9DdRRmhOlhSPGxhgwqyi1fEMrN4CsSKNrv5u+sMqhspA05b3DQJeLDX+UV5ujRHwm0A49NF+h1ZYlrcefz5IMUUisOOw6HiLc/YGLD2jHwSePGdfMwMnrIxbxjCta7/7A91aaN7eYm16KW9erCOwAfJmBSQC6Pbmg5f7rd7rAKVOPxglq7nayXmd+BK53Mal5tltMSgd/0iY6SEtGSEU="
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"expires_at": "2022-07-29T12:57:39.011Z",
|
|
||||||
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtYOUXdid0c09/eYoseLf8qG9fKQ/G2DY9wlSyEZaFMflwZ8ZpLFYigxzfaimpT3A5cbFIdIH2W2sYl5PwsKSs128GBh/rxXUEZlLkIkS+EfxyuMp9ITclxAjCqvFgfJbZHugtB9Ofi6knCEEgjFwMDh2efdpOXkCxtHuPFfnVzDJBbHWdlCCtJesMAnA2jCT7CqCwsi7sW2QxuTarqHP/cHKiBeBIu/SngGUB6eWmvAwERW5x2D+O26w8Z5sQCND3xQ4D868RALiPNG94TyKoJV+jKi0tTUmjGGs/1ksbSGDQb5xqIH0NYKZhoZrczYPNmJX4k7g5BA5RHX8AGORaQIDAQAB\n-----END RSA PUBLIC KEY-----\n",
|
|
||||||
"signature": "Fto/GDqEMTWpNrktWSi3tnP3ZZlo8r4Jled/5PKYRvaL/zksfjB2RK2O8pZL+w5mI2VAViTVAQmSJEF2o/BCb2L0zXp3/hC9VhZj5NTVi4KbHfnfMorj7/WJP2vvMgVxIxgLb3EEQXGS2Mmo0w2ikUVauwXgLWECvVt10KAZnTAWNIvpM8NUoZ2oCCxVimYHBtlwWQ7WvowAatP4ypa7fo3xhQg8Im1hvQDsFTNp58pgnd7l3l99xLj1uYOUJM06HGZJ/Xd0kzzJz44Csh4m50Q0RP4Nq5L+fYPeUx990Z1r1lw0sSayk+vA2Qnxgbs/z6KgkxfhBg7oOlp4ykl9cLC2kA/LdV6igqsdr/KoP4GWxwTA7RgQbhMkDFdmIg1W+gh3XqwASFQK2BAN/eAfmDTf8u9BtOEF7Ehn9uPOaiFiGztyaHxXNIkVSPTG2GXMFSijnd3Ms28jHYVY/67INTnDRmN0//KzBAoTRMe1S5idai19kug4EUVIRKDziipowzCPdbD18trdQGpK0dYOrw9XQiQd4N4V3eItpyAULGiZd8KcjgKo4orqgsUfNyhLI1keig7TyJUE3FkBOfX4hlZBm7Q/Wq4hwarlc5yZIjhsuivKV/q4tcnYYPwjP7kNMRsIApWG+yHmSIo8QfZhBiPxvtWSSLZgoFgnlxfaEko="
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
# About
|
|
||||||
|
|
||||||
This contains test resources for the unit tests. Files in this folder include pre-made cryptographic signatures.
|
|
||||||
|
|
||||||
## Directory structure
|
|
||||||
|
|
||||||
* `valid_signature.json`: Extracted using packet extract from an actual authentication
|
|
||||||
* `incorrect_nonce.json`: Different nonce token simulating that the server expected a different token than signed
|
|
||||||
* `incorrect_salt.json`: Salt sent is different to the content signed by the signature (changed salt field)
|
|
||||||
* `incorrect_signature.json`: Changed signature
|
|
||||||
|
|
||||||
## File content
|
|
||||||
|
|
||||||
* `nonce`: Server generated nonce token
|
|
||||||
* `salt`: Client generated random token that will be signed
|
|
||||||
* `signature`: Nonce and salt signed using the client key from `valid_public_key.json`
|
|
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"nonce": "galNig\u003d\u003d",
|
|
||||||
"signature": {
|
|
||||||
"signature": "JlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d",
|
|
||||||
"salt": -2985008842905108412
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"nonce": "GalNig\u003d\u003d",
|
|
||||||
"signature": {
|
|
||||||
"signature": "JlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d",
|
|
||||||
"salt": -1985008842905108412
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"nonce": "GalNig\u003d\u003d",
|
|
||||||
"signature": {
|
|
||||||
"signature": "jlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d",
|
|
||||||
"salt": -2985008842905108412
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"nonce": "GalNig\u003d\u003d",
|
|
||||||
"signature": {
|
|
||||||
"signature": "JlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d",
|
|
||||||
"salt": -2985008842905108412
|
|
||||||
}
|
|
||||||
}
|
|
146
bungee/pom.xml
146
bungee/pom.xml
@ -1,42 +1,15 @@
|
|||||||
<!--
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
|
||||||
SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.github.games647</groupId>
|
<groupId>com.github.games647</groupId>
|
||||||
<artifactId>fastlogin</artifactId>
|
<artifactId>fastlogin</artifactId>
|
||||||
<version>1.12-SNAPSHOT</version>
|
<version>1.11-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<!--This has to be in lowercase because it's used by plugin.yml-->
|
<!--This have to be in lowercase because it's used by plugin.yml-->
|
||||||
<artifactId>fastlogin.bungee</artifactId>
|
<artifactId>fastlogin.bungee</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
@ -46,11 +19,10 @@
|
|||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
<version>3.5.1</version>
|
<version>3.2.4</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<minimizeJar>true</minimizeJar>
|
|
||||||
|
|
||||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||||
<shadedArtifactAttached>false</shadedArtifactAttached>
|
<shadedArtifactAttached>false</shadedArtifactAttached>
|
||||||
<artifactSet>
|
<artifactSet>
|
||||||
@ -58,7 +30,6 @@
|
|||||||
<!--Those classes are already present in BungeeCord version-->
|
<!--Those classes are already present in BungeeCord version-->
|
||||||
<exclude>net.md-5:bungeecord-config</exclude>
|
<exclude>net.md-5:bungeecord-config</exclude>
|
||||||
<exclude>com.google.code.gson:gson</exclude>
|
<exclude>com.google.code.gson:gson</exclude>
|
||||||
<exclude>com.google.guava:guava</exclude>
|
|
||||||
</excludes>
|
</excludes>
|
||||||
</artifactSet>
|
</artifactSet>
|
||||||
<relocations>
|
<relocations>
|
||||||
@ -71,21 +42,6 @@
|
|||||||
<shadedPattern>fastlogin.slf4j</shadedPattern>
|
<shadedPattern>fastlogin.slf4j</shadedPattern>
|
||||||
</relocation>
|
</relocation>
|
||||||
</relocations>
|
</relocations>
|
||||||
<!-- Rename the service file too to let SLF4J api find our own relocated jdk logger -->
|
|
||||||
<!-- Located in META-INF/services -->
|
|
||||||
<transformers>
|
|
||||||
<transformer
|
|
||||||
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
|
||||||
</transformers>
|
|
||||||
<filters>
|
|
||||||
<filter>
|
|
||||||
<artifact>*:*</artifact>
|
|
||||||
<excludes>
|
|
||||||
<exclude>META-INF/MANIFEST.MF</exclude>
|
|
||||||
<exclude>**/module-info.class</exclude>
|
|
||||||
</excludes>
|
|
||||||
</filter>
|
|
||||||
</filters>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
@ -106,11 +62,13 @@
|
|||||||
</repository>
|
</repository>
|
||||||
|
|
||||||
<repository>
|
<repository>
|
||||||
<id>jitpack.io</id>
|
<id>nukkitx-repo</id>
|
||||||
<url>https://jitpack.io</url>
|
<url>https://repo.nukkitx.com/maven-snapshots/</url>
|
||||||
<snapshots>
|
</repository>
|
||||||
<enabled>false</enabled>
|
|
||||||
</snapshots>
|
<repository>
|
||||||
|
<id>spigotplugins-repo</id>
|
||||||
|
<url>https://maven.gamestrike.de/mvn/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
@ -126,81 +84,16 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.md-5</groupId>
|
<groupId>net.md-5</groupId>
|
||||||
<artifactId>bungeecord-proxy</artifactId>
|
<artifactId>bungeecord-proxy</artifactId>
|
||||||
<version>1.19-R0.1-SNAPSHOT</version>
|
<version>1.16-R0.5-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
<!-- Use our own newer api version -->
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-api</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>mysql</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>net.md-5</groupId>
|
|
||||||
<artifactId>bungeecord-native</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>net.md-5</groupId>
|
|
||||||
<artifactId>bungeecord-query</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>net.md-5</groupId>
|
|
||||||
<artifactId>bungeecord-slf4j</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.apache.maven</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.apache.maven.resolver</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!--Floodgate for Xbox Live Authentication-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.geysermc.floodgate</groupId>
|
|
||||||
<artifactId>api</artifactId>
|
|
||||||
<version>${floodgate.version}</version>
|
|
||||||
<scope>provided</scope>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Bedrock player bridge -->
|
<!-- Bedrock player bridge -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.geysermc.geyser</groupId>
|
<groupId>org.geysermc</groupId>
|
||||||
<artifactId>core</artifactId>
|
<artifactId>floodgate-bungee</artifactId>
|
||||||
<version>${geyser.version}</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- We need the API, but it was excluded above -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.geysermc.geyser</groupId>
|
|
||||||
<artifactId>api</artifactId>
|
|
||||||
<version>${geyser.version}</version>
|
|
||||||
<scope>provided</scope>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!--Login plugin-->
|
<!--Login plugin-->
|
||||||
@ -211,5 +104,12 @@
|
|||||||
<scope>system</scope>
|
<scope>system</scope>
|
||||||
<systemPath>${project.basedir}/lib/BungeeAuth-1.4.jar</systemPath>
|
<systemPath>${project.basedir}/lib/BungeeAuth-1.4.jar</systemPath>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>de.xxschrandxx.bca</groupId>
|
||||||
|
<artifactId>BungeeCordAuthenticator</artifactId>
|
||||||
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -1,32 +1,7 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bungee;
|
package com.github.games647.fastlogin.bungee;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
|
import com.github.games647.fastlogin.core.auth.LoginSession;
|
||||||
|
|
||||||
public class BungeeLoginSession extends LoginSession {
|
public class BungeeLoginSession extends LoginSession {
|
||||||
|
|
||||||
@ -59,10 +34,10 @@ public class BungeeLoginSession extends LoginSession {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized String toString() {
|
public synchronized String toString() {
|
||||||
return this.getClass().getSimpleName() + '{'
|
return this.getClass().getSimpleName() + '{' +
|
||||||
+ "alreadySaved=" + alreadySaved
|
"alreadySaved=" + alreadySaved +
|
||||||
+ ", alreadyLogged=" + alreadyLogged
|
", alreadyLogged=" + alreadyLogged +
|
||||||
+ ", registered=" + registered
|
", registered=" + registered +
|
||||||
+ "} " + super.toString();
|
"} " + super.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,15 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bungee;
|
package com.github.games647.fastlogin.bungee;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
import com.github.games647.fastlogin.core.auth.LoginSource;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
import net.md_5.bungee.api.chat.ComponentBuilder;
|
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
import net.md_5.bungee.api.connection.PendingConnection;
|
import net.md_5.bungee.api.connection.PendingConnection;
|
||||||
import net.md_5.bungee.api.event.PreLoginEvent;
|
import net.md_5.bungee.api.event.PreLoginEvent;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
|
|
||||||
public class BungeeLoginSource implements LoginSource {
|
public class BungeeLoginSource implements LoginSource {
|
||||||
|
|
||||||
private final PendingConnection connection;
|
private final PendingConnection connection;
|
||||||
@ -45,7 +21,7 @@ public class BungeeLoginSource implements LoginSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableOnlinemode() {
|
public void setOnlineMode() {
|
||||||
connection.setOnlineMode(true);
|
connection.setOnlineMode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,8 +47,8 @@ public class BungeeLoginSource implements LoginSource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return this.getClass().getSimpleName() + '{'
|
return this.getClass().getSimpleName() + '{' +
|
||||||
+ "connection=" + connection
|
"connection=" + connection +
|
||||||
+ '}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.github.games647.fastlogin.bungee;
|
||||||
|
|
||||||
|
import com.github.games647.fastlogin.core.SessionManager;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.connection.PendingConnection;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||||
|
import net.md_5.bungee.api.plugin.Listener;
|
||||||
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
|
||||||
|
public class BungeeSessionManager extends SessionManager<PlayerDisconnectEvent, PendingConnection, BungeeLoginSession>
|
||||||
|
implements Listener {
|
||||||
|
|
||||||
|
//todo: memory leak on cancelled login event
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayQuit(PlayerDisconnectEvent disconnectEvent) {
|
||||||
|
ProxiedPlayer player = disconnectEvent.getPlayer();
|
||||||
|
UUID playerId = player.getUniqueId();
|
||||||
|
endPlaySession(playerId);
|
||||||
|
}
|
||||||
|
}
|
@ -1,85 +1,49 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bungee;
|
package com.github.games647.fastlogin.bungee;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bungee.hook.BungeeAuthHook;
|
import com.github.games647.fastlogin.bungee.hook.BungeeAuthHook;
|
||||||
|
import com.github.games647.fastlogin.bungee.hook.BungeeCordAuthenticatorHook;
|
||||||
import com.github.games647.fastlogin.bungee.listener.ConnectListener;
|
import com.github.games647.fastlogin.bungee.listener.ConnectListener;
|
||||||
import com.github.games647.fastlogin.bungee.listener.PluginMessageListener;
|
import com.github.games647.fastlogin.bungee.listener.PluginMessageListener;
|
||||||
import com.github.games647.fastlogin.core.AsyncScheduler;
|
import com.github.games647.fastlogin.core.AsyncScheduler;
|
||||||
import com.github.games647.fastlogin.core.CommonUtil;
|
import com.github.games647.fastlogin.core.CommonUtil;
|
||||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
|
||||||
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService;
|
|
||||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
|
||||||
import com.github.games647.fastlogin.core.hooks.bedrock.GeyserService;
|
|
||||||
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
||||||
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
||||||
import com.github.games647.fastlogin.core.message.NamespaceKey;
|
import com.github.games647.fastlogin.core.message.NamespaceKey;
|
||||||
import com.github.games647.fastlogin.core.message.SuccessMessage;
|
import com.github.games647.fastlogin.core.message.SuccessMessage;
|
||||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||||
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
||||||
import com.google.common.collect.MapMaker;
|
|
||||||
import com.google.common.io.ByteArrayDataOutput;
|
import com.google.common.io.ByteArrayDataOutput;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
|
||||||
import net.md_5.bungee.api.CommandSender;
|
import net.md_5.bungee.api.CommandSender;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
import net.md_5.bungee.api.connection.PendingConnection;
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
import net.md_5.bungee.api.connection.Server;
|
import net.md_5.bungee.api.connection.Server;
|
||||||
import net.md_5.bungee.api.plugin.Listener;
|
|
||||||
import net.md_5.bungee.api.plugin.Plugin;
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
import net.md_5.bungee.api.plugin.PluginManager;
|
import net.md_5.bungee.api.plugin.PluginManager;
|
||||||
import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
|
import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
|
||||||
import org.geysermc.floodgate.api.FloodgateApi;
|
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import org.slf4j.Logger;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.concurrent.ThreadFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BungeeCord version of FastLogin. This plugin keeps track on online mode connections.
|
* BungeeCord version of FastLogin. This plugin keeps track on online mode connections.
|
||||||
*/
|
*/
|
||||||
public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSender> {
|
public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSender> {
|
||||||
|
|
||||||
private final ConcurrentMap<PendingConnection, BungeeLoginSession> session = new MapMaker().weakKeys().makeMap();
|
private final BungeeSessionManager sessionManager = new BungeeSessionManager();
|
||||||
|
|
||||||
private FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core;
|
private FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core;
|
||||||
private AsyncScheduler scheduler;
|
private AsyncScheduler scheduler;
|
||||||
private FloodgateService floodgateService;
|
|
||||||
private GeyserService geyserService;
|
|
||||||
private Logger logger;
|
private Logger logger;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
logger = CommonUtil.initializeLoggerService(getLogger());
|
logger = CommonUtil.createLoggerFromJDK(getLogger());
|
||||||
scheduler = new AsyncScheduler(logger, task -> getProxy().getScheduler().runAsync(this, task));
|
scheduler = new AsyncScheduler(logger, getThreadFactory());
|
||||||
|
|
||||||
core = new FastLoginCore<>(this);
|
core = new FastLoginCore<>(this);
|
||||||
core.load();
|
core.load();
|
||||||
@ -87,18 +51,11 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPluginInstalled("floodgate")) {
|
|
||||||
floodgateService = new FloodgateService(FloodgateApi.getInstance(), core);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPluginInstalled("Geyser-BungeeCord")) {
|
|
||||||
geyserService = new GeyserService(GeyserImpl.getInstance(), core);
|
|
||||||
}
|
|
||||||
|
|
||||||
//events
|
//events
|
||||||
PluginManager pluginManager = getProxy().getPluginManager();
|
PluginManager pluginManager = getProxy().getPluginManager();
|
||||||
|
boolean floodgateAvail = pluginManager.getPlugin("floodgate") != null;
|
||||||
|
ConnectListener connectListener = new ConnectListener(this, core.getRateLimiter(), floodgateAvail);
|
||||||
|
|
||||||
Listener connectListener = new ConnectListener(this, core.getAntiBot());
|
|
||||||
pluginManager.registerListener(this, connectListener);
|
pluginManager.registerListener(this, connectListener);
|
||||||
pluginManager.registerListener(this, new PluginMessageListener(this));
|
pluginManager.registerListener(this, new PluginMessageListener(this));
|
||||||
|
|
||||||
@ -120,30 +77,17 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
|
|||||||
return core;
|
return core;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConcurrentMap<PendingConnection, BungeeLoginSession> getSession() {
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerHook() {
|
private void registerHook() {
|
||||||
try {
|
Plugin BungeeAuth = getProxy().getPluginManager().getPlugin("BungeeAuth");
|
||||||
List<Class<? extends AuthPlugin<ProxiedPlayer>>> hooks = Collections.singletonList(
|
if (BungeeAuth != null) {
|
||||||
BungeeAuthHook.class
|
core.setAuthPluginHook(new BungeeAuthHook());
|
||||||
);
|
logger.info("Hooked into BungeeAuth");
|
||||||
|
}
|
||||||
for (Class<? extends AuthPlugin<ProxiedPlayer>> clazz : hooks) {
|
Plugin BungeeCordAuthenticatorBungee = getProxy().getPluginManager().getPlugin("BungeeCordAuthenticatorBungee");
|
||||||
String pluginName = clazz.getSimpleName();
|
if (BungeeCordAuthenticatorBungee != null) {
|
||||||
pluginName = pluginName.substring(0, pluginName.length() - "Hook".length());
|
logger.info("Try to hook into BungeeCordAuthenticatorBungee...");
|
||||||
//uses only member classes which uses AuthPlugin interface (skip interfaces)
|
BungeeCordAuthenticatorHook hook = new BungeeCordAuthenticatorHook(BungeeCordAuthenticatorBungee, logger);
|
||||||
Plugin plugin = getProxy().getPluginManager().getPlugin(pluginName);
|
core.setAuthPluginHook(hook);
|
||||||
if (plugin != null) {
|
|
||||||
logger.info("Hooking into auth plugin: {}", pluginName);
|
|
||||||
core.setAuthPluginHook(
|
|
||||||
clazz.getDeclaredConstructor(FastLoginBungee.class).newInstance(this));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (ReflectiveOperationException ex) {
|
|
||||||
logger.error("Couldn't load the auth hook class", ex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,25 +136,4 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
|
|||||||
public AsyncScheduler getScheduler() {
|
public AsyncScheduler getScheduler() {
|
||||||
return scheduler;
|
return scheduler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isPluginInstalled(String name) {
|
|
||||||
return getProxy().getPluginManager().getPlugin(name) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FloodgateService getFloodgateService() {
|
|
||||||
return floodgateService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GeyserService getGeyserService() {
|
|
||||||
return geyserService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BedrockService<?> getBedrockService() {
|
|
||||||
if (floodgateService != null) {
|
|
||||||
return floodgateService;
|
|
||||||
}
|
|
||||||
return geyserService;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,8 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bungee.event;
|
package com.github.games647.fastlogin.bungee.event;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
|
||||||
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
|
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
|
import com.github.games647.fastlogin.core.auth.LoginSession;
|
||||||
|
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
|
||||||
import net.md_5.bungee.api.plugin.Cancellable;
|
import net.md_5.bungee.api.plugin.Cancellable;
|
||||||
import net.md_5.bungee.api.plugin.Event;
|
import net.md_5.bungee.api.plugin.Event;
|
||||||
|
|
||||||
|
@ -1,33 +1,8 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bungee.event;
|
package com.github.games647.fastlogin.bungee.event;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
|
||||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
|
import com.github.games647.fastlogin.core.auth.LoginSource;
|
||||||
|
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
||||||
import net.md_5.bungee.api.plugin.Event;
|
import net.md_5.bungee.api.plugin.Event;
|
||||||
|
|
||||||
public class BungeeFastLoginPreLoginEvent extends Event implements FastLoginPreLoginEvent {
|
public class BungeeFastLoginPreLoginEvent extends Event implements FastLoginPreLoginEvent {
|
||||||
|
@ -1,32 +1,7 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bungee.event;
|
package com.github.games647.fastlogin.bungee.event;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
|
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
|
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
|
||||||
import net.md_5.bungee.api.plugin.Event;
|
import net.md_5.bungee.api.plugin.Event;
|
||||||
|
|
||||||
public class BungeeFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {
|
public class BungeeFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {
|
||||||
@ -45,7 +20,7 @@ public class BungeeFastLoginPremiumToggleEvent extends Event implements FastLogi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PremiumToggleReason getReason() {
|
public FastLoginPremiumToggleEvent.PremiumToggleReason getReason() {
|
||||||
return reason;
|
return reason;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,23 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bungee.hook;
|
package com.github.games647.fastlogin.bungee.hook;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
|
||||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||||
|
|
||||||
import me.vik1395.BungeeAuth.Main;
|
import me.vik1395.BungeeAuth.Main;
|
||||||
import me.vik1395.BungeeAuthAPI.RequestHandler;
|
import me.vik1395.BungeeAuthAPI.RequestHandler;
|
||||||
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GitHub:
|
* GitHub: https://github.com/vik1395/BungeeAuth-Minecraft
|
||||||
* <a href="https://github.com/vik1395/BungeeAuth-Minecraft">...</a>
|
*
|
||||||
* <p>
|
|
||||||
* Project page:
|
* Project page:
|
||||||
* <p>
|
*
|
||||||
* Spigot:
|
* Spigot: https://www.spigotmc.org/resources/bungeeauth.493/
|
||||||
* <a href="https://www.spigotmc.org/resources/bungeeauth.493/">...</a>
|
|
||||||
*/
|
*/
|
||||||
public class BungeeAuthHook implements AuthPlugin<ProxiedPlayer> {
|
public class BungeeAuthHook implements AuthPlugin<ProxiedPlayer> {
|
||||||
|
|
||||||
private final RequestHandler requestHandler = new RequestHandler();
|
private final RequestHandler requestHandler = new RequestHandler();
|
||||||
|
|
||||||
public BungeeAuthHook(FastLoginBungee plugin) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean forceLogin(ProxiedPlayer player) {
|
public boolean forceLogin(ProxiedPlayer player) {
|
||||||
String playerName = player.getName();
|
String playerName = player.getName();
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
package com.github.games647.fastlogin.bungee.hook;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
import de.xxschrandxx.bca.bungee.BungeeCordAuthenticatorBungee;
|
||||||
|
import de.xxschrandxx.bca.bungee.api.BungeeCordAuthenticatorBungeeAPI;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GitHub:
|
||||||
|
* https://github.com/xXSchrandXx/SpigotPlugins/tree/master/BungeeCordAuthenticator
|
||||||
|
*
|
||||||
|
* Project page:
|
||||||
|
*
|
||||||
|
* Spigot: https://www.spigotmc.org/resources/bungeecordauthenticator.87669/
|
||||||
|
*/
|
||||||
|
public class BungeeCordAuthenticatorHook implements AuthPlugin<ProxiedPlayer> {
|
||||||
|
|
||||||
|
public final BungeeCordAuthenticatorBungeeAPI api;
|
||||||
|
|
||||||
|
public BungeeCordAuthenticatorHook(Plugin plugin, Logger logger) {
|
||||||
|
BungeeCordAuthenticatorBungee bcab = (BungeeCordAuthenticatorBungee) plugin;
|
||||||
|
api = bcab.getAPI();
|
||||||
|
logger.info("BungeeCordAuthenticatorHook | Hooked successful!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean forceLogin(ProxiedPlayer player) {
|
||||||
|
if (api.isAuthenticated(player)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
api.setAuthenticated(player);
|
||||||
|
}
|
||||||
|
catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRegistered(String playerName) {
|
||||||
|
try {
|
||||||
|
return api.getSQL().checkPlayerEntry(playerName);
|
||||||
|
}
|
||||||
|
catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean forceRegister(ProxiedPlayer player, String password) {
|
||||||
|
try {
|
||||||
|
return api.createPlayerEntry(player, password);
|
||||||
|
}
|
||||||
|
catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,43 +1,21 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bungee.listener;
|
package com.github.games647.fastlogin.bungee.listener;
|
||||||
|
|
||||||
import com.github.games647.craftapi.UUIDAdapter;
|
import com.github.games647.craftapi.UUIDAdapter;
|
||||||
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||||
import com.github.games647.fastlogin.bungee.task.AsyncPremiumCheck;
|
import com.github.games647.fastlogin.bungee.task.AsyncPremiumCheck;
|
||||||
import com.github.games647.fastlogin.bungee.task.FloodgateAuthTask;
|
|
||||||
import com.github.games647.fastlogin.bungee.task.ForceLoginTask;
|
import com.github.games647.fastlogin.bungee.task.ForceLoginTask;
|
||||||
import com.github.games647.fastlogin.core.antibot.AntiBotService;
|
import com.github.games647.fastlogin.core.auth.RateLimiter;
|
||||||
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
|
|
||||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
|
||||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
|
import com.github.games647.fastlogin.core.auth.LoginSession;
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodHandles.Lookup;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import net.md_5.bungee.api.connection.PendingConnection;
|
import net.md_5.bungee.api.connection.PendingConnection;
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
import net.md_5.bungee.api.connection.Server;
|
import net.md_5.bungee.api.connection.Server;
|
||||||
@ -48,20 +26,14 @@ import net.md_5.bungee.api.event.ServerConnectedEvent;
|
|||||||
import net.md_5.bungee.api.plugin.Listener;
|
import net.md_5.bungee.api.plugin.Listener;
|
||||||
import net.md_5.bungee.connection.InitialHandler;
|
import net.md_5.bungee.connection.InitialHandler;
|
||||||
import net.md_5.bungee.connection.LoginResult;
|
import net.md_5.bungee.connection.LoginResult;
|
||||||
|
import net.md_5.bungee.connection.LoginResult.Property;
|
||||||
import net.md_5.bungee.event.EventHandler;
|
import net.md_5.bungee.event.EventHandler;
|
||||||
import net.md_5.bungee.event.EventPriority;
|
import net.md_5.bungee.event.EventPriority;
|
||||||
import net.md_5.bungee.protocol.Property;
|
|
||||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
import org.geysermc.floodgate.FloodgateAPI;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandle;
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.lang.invoke.MethodHandles.Lookup;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables online mode logins for specified users and sends plugin message to the Bukkit version of this plugin in
|
* Enables online mode logins for specified users and sends plugin message to the Bukkit version of this plugin in
|
||||||
* order to clear that the connection is online mode.
|
* order to clear that the connection is online mode.
|
||||||
@ -69,67 +41,64 @@ import java.util.UUID;
|
|||||||
public class ConnectListener implements Listener {
|
public class ConnectListener implements Listener {
|
||||||
|
|
||||||
private static final String UUID_FIELD_NAME = "uniqueId";
|
private static final String UUID_FIELD_NAME = "uniqueId";
|
||||||
protected static final MethodHandle UNIQUE_ID_SETTER;
|
private static final boolean initialHandlerClazzFound;
|
||||||
|
private static final MethodHandle uniqueIdSetter;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
MethodHandle setHandle = null;
|
MethodHandle setHandle = null;
|
||||||
|
boolean handlerFound = false;
|
||||||
try {
|
try {
|
||||||
Lookup lookup = MethodHandles.lookup();
|
Lookup lookup = MethodHandles.lookup();
|
||||||
|
|
||||||
Class.forName("net.md_5.bungee.connection.InitialHandler");
|
Class.forName("net.md_5.bungee.connection.InitialHandler");
|
||||||
|
handlerFound = true;
|
||||||
|
|
||||||
Field uuidField = InitialHandler.class.getDeclaredField(UUID_FIELD_NAME);
|
Field uuidField = InitialHandler.class.getDeclaredField(UUID_FIELD_NAME);
|
||||||
uuidField.setAccessible(true);
|
uuidField.setAccessible(true);
|
||||||
setHandle = lookup.unreflectSetter(uuidField);
|
setHandle = lookup.unreflectSetter(uuidField);
|
||||||
} catch (ReflectiveOperationException reflectiveOperationException) {
|
} catch (ClassNotFoundException classNotFoundException) {
|
||||||
Logger logger = LoggerFactory.getLogger(ConnectListener.class);
|
Logger logger = LoggerFactory.getLogger(ConnectListener.class);
|
||||||
logger.error(
|
logger.error(
|
||||||
"Cannot find Bungee initial handler; Disabling premium UUID and skin won't work.",
|
"Cannot find Bungee initial handler; Disabling premium UUID and skin won't work.",
|
||||||
reflectiveOperationException
|
classNotFoundException
|
||||||
);
|
);
|
||||||
|
} catch (ReflectiveOperationException reflectiveOperationException) {
|
||||||
|
reflectiveOperationException.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
UNIQUE_ID_SETTER = setHandle;
|
initialHandlerClazzFound = handlerFound;
|
||||||
|
uniqueIdSetter = setHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final FastLoginBungee plugin;
|
private final FastLoginBungee plugin;
|
||||||
private final AntiBotService antiBotService;
|
private final RateLimiter rateLimiter;
|
||||||
private final Property[] emptyProperties = {};
|
private final Property[] emptyProperties = {};
|
||||||
|
private final boolean floodGateAvailable;
|
||||||
|
|
||||||
public ConnectListener(FastLoginBungee plugin, AntiBotService antiBotService) {
|
public ConnectListener(FastLoginBungee plugin, RateLimiter rateLimiter, boolean floodgateAvailable) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.antiBotService = antiBotService;
|
this.rateLimiter = rateLimiter;
|
||||||
|
this.floodGateAvailable = floodgateAvailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPreLogin(PreLoginEvent preLoginEvent) {
|
public void onPreLogin(PreLoginEvent preLoginEvent) {
|
||||||
PendingConnection connection = preLoginEvent.getConnection();
|
PendingConnection connection = preLoginEvent.getConnection();
|
||||||
if (preLoginEvent.isCancelled()) {
|
if (preLoginEvent.isCancelled() || isBedrockPlayer(connection.getUniqueId())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InetSocketAddress address = preLoginEvent.getConnection().getAddress();
|
if (!rateLimiter.tryAcquire()) {
|
||||||
String username = connection.getName();
|
plugin.getLog().warn("Join limit hit - Ignoring player {}", connection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String username = connection.getName();
|
||||||
plugin.getLog().info("Incoming login request for {} from {}", username, connection.getSocketAddress());
|
plugin.getLog().info("Incoming login request for {} from {}", username, connection.getSocketAddress());
|
||||||
|
|
||||||
Action action = antiBotService.onIncomingConnection(address, username);
|
preLoginEvent.registerIntent(plugin);
|
||||||
switch (action) {
|
Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username);
|
||||||
case Ignore:
|
plugin.getScheduler().runAsync(asyncPremiumCheck);
|
||||||
// just ignore
|
|
||||||
return;
|
|
||||||
case Block:
|
|
||||||
String message = plugin.getCore().getMessage("kick-antibot");
|
|
||||||
preLoginEvent.setCancelReason(TextComponent.fromLegacyText(message));
|
|
||||||
preLoginEvent.setCancelled(true);
|
|
||||||
break;
|
|
||||||
case Continue:
|
|
||||||
default:
|
|
||||||
preLoginEvent.registerIntent(plugin);
|
|
||||||
Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username);
|
|
||||||
plugin.getScheduler().runAsync(asyncPremiumCheck);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
@ -152,8 +121,8 @@ public class ConnectListener implements Listener {
|
|||||||
StoredProfile playerProfile = session.getProfile();
|
StoredProfile playerProfile = session.getProfile();
|
||||||
playerProfile.setId(verifiedUUID);
|
playerProfile.setId(verifiedUUID);
|
||||||
|
|
||||||
// BungeeCord will do this automatically so override it on disabled option
|
// bungeecord will do this automatically so override it on disabled option
|
||||||
if (UNIQUE_ID_SETTER != null) {
|
if (uniqueIdSetter != null) {
|
||||||
InitialHandler initialHandler = (InitialHandler) connection;
|
InitialHandler initialHandler = (InitialHandler) connection;
|
||||||
|
|
||||||
if (!plugin.getCore().getConfig().get("premiumUuid", true)) {
|
if (!plugin.getCore().getConfig().get("premiumUuid", true)) {
|
||||||
@ -169,22 +138,22 @@ public class ConnectListener implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setOfflineId(InitialHandler connection, String username) {
|
private void setOfflineId(InitialHandler connection, String username) {
|
||||||
try {
|
try {
|
||||||
UUID oldPremiumId = connection.getUniqueId();
|
final UUID oldPremiumId = connection.getUniqueId();
|
||||||
UUID offlineUUID = UUIDAdapter.generateOfflineId(username);
|
final UUID offlineUUID = UUIDAdapter.generateOfflineId(username);
|
||||||
|
|
||||||
// BungeeCord only allows setting the UUID in PreLogin events and before requesting online mode
|
// BungeeCord only allows setting the UUID in PreLogin events and before requesting online mode
|
||||||
// However if online mode is requested, it will override previous values
|
// However if online mode is requested, it will override previous values
|
||||||
// So we have to do it with reflection
|
// So we have to do it with reflection
|
||||||
UNIQUE_ID_SETTER.invokeExact(connection, offlineUUID);
|
uniqueIdSetter.invokeExact(connection, offlineUUID);
|
||||||
|
|
||||||
String format = "Overridden UUID from {} to {} (based of {}) on {}";
|
String format = "Overridden UUID from {} to {} (based of {}) on {}";
|
||||||
plugin.getLog().info(format, oldPremiumId, offlineUUID, username, connection);
|
plugin.getLog().info(format, oldPremiumId, offlineUUID, username, connection);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
plugin.getLog().error("Failed to set offline uuid of {}", username, ex);
|
plugin.getLog().error("Failed to set offline uuid of {}", username, ex);
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
// throw remaining exceptions like out of memory that we shouldn't handle ourselves
|
// throw remaining exceptions like outofmemory that we shouldn't handle ourself
|
||||||
Throwables.throwIfUnchecked(throwable);
|
Throwables.throwIfUnchecked(throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,16 +163,6 @@ public class ConnectListener implements Listener {
|
|||||||
ProxiedPlayer player = serverConnectedEvent.getPlayer();
|
ProxiedPlayer player = serverConnectedEvent.getPlayer();
|
||||||
Server server = serverConnectedEvent.getServer();
|
Server server = serverConnectedEvent.getServer();
|
||||||
|
|
||||||
FloodgateService floodgateService = plugin.getFloodgateService();
|
|
||||||
if (floodgateService != null) {
|
|
||||||
FloodgatePlayer floodgatePlayer = floodgateService.getBedrockPlayer(player.getUniqueId());
|
|
||||||
if (floodgatePlayer != null) {
|
|
||||||
Runnable floodgateAuthTask = new FloodgateAuthTask(plugin.getCore(), player, floodgatePlayer, server);
|
|
||||||
plugin.getScheduler().runAsync(floodgateAuthTask);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BungeeLoginSession session = plugin.getSession().get(player.getPendingConnection());
|
BungeeLoginSession session = plugin.getSession().get(player.getPendingConnection());
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
return;
|
return;
|
||||||
@ -222,4 +181,11 @@ public class ConnectListener implements Listener {
|
|||||||
plugin.getSession().remove(player.getPendingConnection());
|
plugin.getSession().remove(player.getPendingConnection());
|
||||||
plugin.getCore().getPendingConfirms().remove(player.getUniqueId());
|
plugin.getCore().getPendingConfirms().remove(player.getUniqueId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isBedrockPlayer(UUID correctedUUID) {
|
||||||
|
// Floodgate will set a correct UUID at the beginning of the PreLoginEvent
|
||||||
|
// and will cancel the online mode login for those players
|
||||||
|
// Therefore we just ignore those
|
||||||
|
return floodGateAvailable && FloodgateAPI.isBedrockPlayer(correctedUUID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,18 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bungee.listener;
|
package com.github.games647.fastlogin.bungee.listener;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||||
import com.github.games647.fastlogin.bungee.task.AsyncToggleMessage;
|
import com.github.games647.fastlogin.bungee.task.AsyncToggleMessage;
|
||||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
||||||
import com.github.games647.fastlogin.core.message.NamespaceKey;
|
import com.github.games647.fastlogin.core.message.NamespaceKey;
|
||||||
import com.github.games647.fastlogin.core.message.SuccessMessage;
|
import com.github.games647.fastlogin.core.message.SuccessMessage;
|
||||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
|
||||||
import com.google.common.io.ByteArrayDataInput;
|
import com.google.common.io.ByteArrayDataInput;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import net.md_5.bungee.api.CommandSender;
|
import net.md_5.bungee.api.CommandSender;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
@ -44,8 +21,6 @@ import net.md_5.bungee.api.event.PluginMessageEvent;
|
|||||||
import net.md_5.bungee.api.plugin.Listener;
|
import net.md_5.bungee.api.plugin.Listener;
|
||||||
import net.md_5.bungee.event.EventHandler;
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public class PluginMessageListener implements Listener {
|
public class PluginMessageListener implements Listener {
|
||||||
|
|
||||||
private final FastLoginBungee plugin;
|
private final FastLoginBungee plugin;
|
||||||
@ -68,7 +43,7 @@ public class PluginMessageListener implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//the client shouldn't be able to read the messages in order to know something about server internal states
|
//the client shouldn't be able to read the messages in order to know something about server internal states
|
||||||
//moreover the client shouldn't be able to fake a running premium check by sending the result message
|
//moreover the client shouldn't be able fake a running premium check by sending the result message
|
||||||
pluginMessageEvent.setCancelled(true);
|
pluginMessageEvent.setCancelled(true);
|
||||||
|
|
||||||
if (!(pluginMessageEvent.getSender() instanceof Server)) {
|
if (!(pluginMessageEvent.getSender() instanceof Server)) {
|
||||||
@ -115,15 +90,7 @@ public class PluginMessageListener implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onSuccessMessage(ProxiedPlayer forPlayer) {
|
private void onSuccessMessage(ProxiedPlayer forPlayer) {
|
||||||
boolean shouldPersist = forPlayer.getPendingConnection().isOnlineMode();
|
if (forPlayer.getPendingConnection().isOnlineMode()) {
|
||||||
|
|
||||||
FloodgateService floodgateService = plugin.getFloodgateService();
|
|
||||||
if (!shouldPersist && floodgateService != null) {
|
|
||||||
// always save floodgate players to lock this username
|
|
||||||
shouldPersist = floodgateService.isBedrockPlayer(forPlayer.getUniqueId());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldPersist) {
|
|
||||||
//bukkit module successfully received and force logged in the user
|
//bukkit module successfully received and force logged in the user
|
||||||
//update only on success to prevent corrupt data
|
//update only on success to prevent corrupt data
|
||||||
BungeeLoginSession loginSession = plugin.getSession().get(forPlayer.getPendingConnection());
|
BungeeLoginSession loginSession = plugin.getSession().get(forPlayer.getPendingConnection());
|
||||||
|
@ -1,37 +1,13 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bungee.task;
|
package com.github.games647.fastlogin.bungee.task;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||||
import com.github.games647.fastlogin.bungee.BungeeLoginSource;
|
import com.github.games647.fastlogin.bungee.BungeeLoginSource;
|
||||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||||
import com.github.games647.fastlogin.bungee.event.BungeeFastLoginPreLoginEvent;
|
import com.github.games647.fastlogin.bungee.event.BungeeFastLoginPreLoginEvent;
|
||||||
import com.github.games647.fastlogin.core.shared.JoinManagement;
|
|
||||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
|
import com.github.games647.fastlogin.core.auth.JoinManagement;
|
||||||
|
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
||||||
|
|
||||||
import net.md_5.bungee.api.CommandSender;
|
import net.md_5.bungee.api.CommandSender;
|
||||||
import net.md_5.bungee.api.connection.PendingConnection;
|
import net.md_5.bungee.api.connection.PendingConnection;
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
@ -48,7 +24,7 @@ public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, CommandSend
|
|||||||
|
|
||||||
public AsyncPremiumCheck(FastLoginBungee plugin, PreLoginEvent preLoginEvent, PendingConnection connection,
|
public AsyncPremiumCheck(FastLoginBungee plugin, PreLoginEvent preLoginEvent, PendingConnection connection,
|
||||||
String username) {
|
String username) {
|
||||||
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
|
super(plugin.getCore(), plugin.getCore().getAuthPluginHook());
|
||||||
|
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.preLoginEvent = preLoginEvent;
|
this.preLoginEvent = preLoginEvent;
|
||||||
@ -77,7 +53,7 @@ public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, CommandSend
|
|||||||
@Override
|
@Override
|
||||||
public void requestPremiumLogin(BungeeLoginSource source, StoredProfile profile,
|
public void requestPremiumLogin(BungeeLoginSource source, StoredProfile profile,
|
||||||
String username, boolean registered) {
|
String username, boolean registered) {
|
||||||
source.enableOnlinemode();
|
source.setOnlineMode();
|
||||||
plugin.getSession().put(source.getConnection(), new BungeeLoginSession(username, registered, profile));
|
plugin.getSession().put(source.getConnection(), new BungeeLoginSession(username, registered, profile));
|
||||||
|
|
||||||
String ip = source.getAddress().getAddress().getHostAddress();
|
String ip = source.getAddress().getAddress().getHostAddress();
|
||||||
|
@ -1,35 +1,11 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bungee.task;
|
package com.github.games647.fastlogin.bungee.task;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||||
import com.github.games647.fastlogin.bungee.event.BungeeFastLoginPremiumToggleEvent;
|
import com.github.games647.fastlogin.bungee.event.BungeeFastLoginPremiumToggleEvent;
|
||||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
|
||||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
|
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
|
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||||
|
|
||||||
|
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
|
||||||
import net.md_5.bungee.api.CommandSender;
|
import net.md_5.bungee.api.CommandSender;
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
@ -72,8 +48,8 @@ public class AsyncToggleMessage implements Runnable {
|
|||||||
playerProfile.setPremium(false);
|
playerProfile.setPremium(false);
|
||||||
playerProfile.setId(null);
|
playerProfile.setId(null);
|
||||||
core.getStorage().save(playerProfile);
|
core.getStorage().save(playerProfile);
|
||||||
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName()))
|
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName())) ?
|
||||||
? PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
|
PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
|
||||||
core.getPlugin().getProxy().getPluginManager().callEvent(
|
core.getPlugin().getProxy().getPluginManager().callEvent(
|
||||||
new BungeeFastLoginPremiumToggleEvent(playerProfile, reason));
|
new BungeeFastLoginPremiumToggleEvent(playerProfile, reason));
|
||||||
sendMessage("remove-premium");
|
sendMessage("remove-premium");
|
||||||
@ -88,8 +64,8 @@ public class AsyncToggleMessage implements Runnable {
|
|||||||
|
|
||||||
playerProfile.setPremium(true);
|
playerProfile.setPremium(true);
|
||||||
core.getStorage().save(playerProfile);
|
core.getStorage().save(playerProfile);
|
||||||
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName()))
|
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName())) ?
|
||||||
? PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
|
PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
|
||||||
core.getPlugin().getProxy().getPluginManager().callEvent(
|
core.getPlugin().getProxy().getPluginManager().callEvent(
|
||||||
new BungeeFastLoginPremiumToggleEvent(playerProfile, reason));
|
new BungeeFastLoginPremiumToggleEvent(playerProfile, reason));
|
||||||
sendMessage("add-premium");
|
sendMessage("add-premium");
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bungee.task;
|
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
|
||||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
|
||||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
|
||||||
import com.github.games647.fastlogin.core.shared.FloodgateManagement;
|
|
||||||
import net.md_5.bungee.api.CommandSender;
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|
||||||
import net.md_5.bungee.api.connection.Server;
|
|
||||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class FloodgateAuthTask
|
|
||||||
extends FloodgateManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> {
|
|
||||||
|
|
||||||
private final Server server;
|
|
||||||
|
|
||||||
public FloodgateAuthTask(FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core, ProxiedPlayer player,
|
|
||||||
FloodgatePlayer floodgatePlayer, Server server) {
|
|
||||||
super(core, player, floodgatePlayer);
|
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void startLogin() {
|
|
||||||
BungeeLoginSession session = new BungeeLoginSession(player.getName(), isRegistered, profile);
|
|
||||||
core.getPlugin().getSession().put(player.getPendingConnection(), session);
|
|
||||||
|
|
||||||
// run login task
|
|
||||||
Runnable forceLoginTask = new ForceLoginTask(core.getPlugin().getCore(), player, server, session,
|
|
||||||
isAutoAuthAllowed(autoLoginFloodgate));
|
|
||||||
core.getPlugin().getScheduler().runAsync(forceLoginTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getName(ProxiedPlayer player) {
|
|
||||||
return player.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected UUID getUUID(ProxiedPlayer player) {
|
|
||||||
return player.getUniqueId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected InetSocketAddress getAddress(ProxiedPlayer player) {
|
|
||||||
return player.getAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,68 +1,34 @@
|
|||||||
/*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015-2023 games647 and contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package com.github.games647.fastlogin.bungee.task;
|
package com.github.games647.fastlogin.bungee.task;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||||
import com.github.games647.fastlogin.bungee.event.BungeeFastLoginAutoLoginEvent;
|
import com.github.games647.fastlogin.bungee.event.BungeeFastLoginAutoLoginEvent;
|
||||||
|
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||||
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
||||||
import com.github.games647.fastlogin.core.message.LoginActionMessage;
|
import com.github.games647.fastlogin.core.message.LoginActionMessage;
|
||||||
import com.github.games647.fastlogin.core.message.LoginActionMessage.Type;
|
import com.github.games647.fastlogin.core.message.LoginActionMessage.Type;
|
||||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||||
import com.github.games647.fastlogin.core.shared.ForceLoginManagement;
|
import com.github.games647.fastlogin.core.auth.ForceLoginManagement;
|
||||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
import com.github.games647.fastlogin.core.auth.LoginSession;
|
||||||
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
|
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
|
||||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import net.md_5.bungee.api.CommandSender;
|
import net.md_5.bungee.api.CommandSender;
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
import net.md_5.bungee.api.connection.Server;
|
import net.md_5.bungee.api.connection.Server;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class ForceLoginTask
|
public class ForceLoginTask
|
||||||
extends ForceLoginManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> {
|
extends ForceLoginManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> {
|
||||||
|
|
||||||
private final Server server;
|
private final Server server;
|
||||||
|
|
||||||
//treat player as if they had a premium account, even when they don't
|
|
||||||
//use for Floodgate auto login/register
|
|
||||||
private final boolean forcedOnlineMode;
|
|
||||||
|
|
||||||
public ForceLoginTask(FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core,
|
public ForceLoginTask(FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core,
|
||||||
ProxiedPlayer player, Server server, BungeeLoginSession session, boolean forcedOnlineMode) {
|
ProxiedPlayer player, Server server, BungeeLoginSession session) {
|
||||||
super(core, player, session);
|
super(core, player, session);
|
||||||
|
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.forcedOnlineMode = forcedOnlineMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ForceLoginTask(FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core, ProxiedPlayer player,
|
|
||||||
Server server, BungeeLoginSession session) {
|
|
||||||
this(core, player, server, session, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -125,6 +91,6 @@ public class ForceLoginTask
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isOnlineMode() {
|
public boolean isOnlineMode() {
|
||||||
return forcedOnlineMode || player.getPendingConnection().isOnlineMode();
|
return player.getPendingConnection().isOnlineMode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user