diff --git a/README.md b/README.md
index 9e467de..c0482ff 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,7 @@ You can use the current version and give us a feedback about what needs to be ch
## 👥 Contributors
urhatedjack - logo design
+[KotreQ](https://github.com/KotreQ) - help with the QR code generation
Thanks to **Discord support team**:
- [Blavez](https://github.com/Blavezz)
@@ -67,7 +68,7 @@ NavAuth is licensed under the GNU AGPL v3. See the license file for more informa
[](https://www.gnu.org/licenses/agpl-3.0.en.html)
## 💡 TODO List
-More planned features are described in [Documentation](https://navio1430.github.io/NavAuth/offer.html#%F0%9F%9A%80-planned-features)
+More planned features are described in [Documentation](https://navio1430.github.io/NavAuth/docs/offer.html#%F0%9F%9A%80-planned-features)
- readme:
- add banner
- github/gh actions:
diff --git a/build.gradle.kts b/build.gradle.kts
index cf3fd86..09f0dbd 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -11,7 +11,7 @@ repositories {
allprojects {
group = "pl.spcode.navauth"
- version = "0.1.3-SNAPSHOT"
+ version = "0.1.4-SNAPSHOT"
}
tasks.register("formatAll") {
diff --git a/docs/docs/index.md b/docs/docs/index.md
index ccf4f75..8d9b4cb 100644
--- a/docs/docs/index.md
+++ b/docs/docs/index.md
@@ -25,7 +25,7 @@ features:
- icon: âš¡
title: Setup & Run the plugin
details: Go through the detailed description about how to configure and run NavAuth.
- link: /setup # todo
+ link: /setup
- icon: 🚀
title: Migrate now
details: Check how to migrate from other plugins like LibreLogin, AuthMe and FastLogin.
@@ -33,7 +33,7 @@ features:
- icon: 🧩
title: Requirements
details: Check the requirements needed to run NavAuth.
- link: /requirements # todo
+ link: /requirements
- icon:
src: https://pngimg.com/d/discord_PNG3.png
title: Need support?
diff --git a/docs/docs/navauth-release.md b/docs/docs/navauth-release.md
index 3b26824..eba8aa9 100644
--- a/docs/docs/navauth-release.md
+++ b/docs/docs/navauth-release.md
@@ -1,4 +1,3 @@
# NavAuth Release
-First release of NavAuth is still under development.
-Please join our discord server to be notified about version 1.0
+NavAuth version 1.0 is still under development. As of now you can use versions **0.X** and give us your feedback on our **Discord** server.
\ No newline at end of file
diff --git a/docs/docs/requirements.md b/docs/docs/requirements.md
new file mode 100644
index 0000000..93a23bf
--- /dev/null
+++ b/docs/docs/requirements.md
@@ -0,0 +1,13 @@
+# Requirements
+
+## What is required to run NavAuth?
+- Velocity (preferably the newest versions)
+- Limbo server e.g., NanoLimbo, PicoLimbo
+- You can use [PicoLimbo velocity plugin](https://modrinth.com/plugin/picolimbo-java-wrapper) for simplicity.
+::: danger
+DO NOT USE NORMAL PAPER/LOBBY SERVER AS LIMBO. IT CAN CAUSE A MAJOR SECURITY ISSUE.
+:::
+
+## Modern forwarding is required
+Please set **Velocity** forwarding to `MODERN`.
+All backend servers must support `MODERN` forward too (**1.13.1+ only**).
diff --git a/docs/docs/setup.md b/docs/docs/setup.md
new file mode 100644
index 0000000..3449de5
--- /dev/null
+++ b/docs/docs/setup.md
@@ -0,0 +1,6 @@
+# Setup
+
+Detailed setup tutorial is still under construction.
+Follow [Requirements page](/requirements) for requirements details.
+If you have basic knowledge of how **Velocity** proxy works, then it shouldn't
+be a problem for you to figure it out how to make things work.
\ No newline at end of file
diff --git a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/application/auth/session/AuthSessionService.kt b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/application/auth/session/AuthSessionService.kt
index b812187..10f7089 100644
--- a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/application/auth/session/AuthSessionService.kt
+++ b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/application/auth/session/AuthSessionService.kt
@@ -52,9 +52,7 @@ open class AuthSessionService
{
fun closeSession(uniqueSessionId: UniqueSessionId): Boolean {
val session = sessionsMap.remove(uniqueSessionId)
if (session != null) {
- if (session.playerAdapter.isOnline()) {
- session.playerAdapter.disconnect(DisconnectReason.AUTH_SESSION_CLOSED)
- }
+ session.playerAdapter.disconnect(DisconnectReason.AUTH_SESSION_CLOSED)
session.onInvalidate()
logger.debug(
"invalidated auth session (type='{}') with ID {}",
diff --git a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/config/MessagesConfig.kt b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/config/MessagesConfig.kt
index 08fad16..14f8175 100644
--- a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/config/MessagesConfig.kt
+++ b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/config/MessagesConfig.kt
@@ -32,7 +32,9 @@ import pl.spcode.navauth.common.component.TextComponent
open class MessagesConfig : OkaeriConfig() {
var supportFooter =
- TextComponent("
For support please join our discord: https://dc.spcode.pl/")
+ TextComponent("
For support please join our discord: https://dc.yourwebsite.net/")
+
+ var invalidUsernameError = TextComponent("Invalid username.")
var usernameRequiredError =
TextComponent(
@@ -59,21 +61,50 @@ open class MessagesConfig : OkaeriConfig() {
var registerTimeExceededError =
TextComponent("You've exceeded register time, please try again")
+ var loginTooManyAttemptsError =
+ TextComponent("Too many login attempts. Please try again later.")
+
+ var adminCopyPasswordText =
+ "CLICK HERE TO COPY"
+
+ var yourAccountDataHasBeenMigrated =
+ TextComponent("Your account data has been migrated to '%USERNAME%'.")
+
@Comment(
"Notifications which use multification library.",
"Here you can use chat messages, action bars, sounds etc. combined.",
- "To learn more about multification please read https://navio1430.github.io/NavAuth/configuration/multification.html",
+ "To learn more about multification please read https://navio1430.github.io/NavAuth/docs/configuration/multification.html",
)
var multification = NoticesConfig()
class NoticesConfig : OkaeriConfig() {
+ var passwordRequiredError: Notice = Notice.chat("Please provide your current password.")
+ var twoFactorAlreadyEnabledError: Notice =
+ Notice.chat("Your account has 2FA enabled already!")
+
var missingPermissionError: Notice =
Notice.chat("You don't have permission to execute this command.")
var invalidUsageError: Notice = Notice.chat("Invalid command usage!")
@Comment("Invalid usage scheme line (single text component only).")
var invalidUsageLine: TextComponent = TextComponent(" • %SCHEME%")
+ var cantUseThisCommandNowError: Notice = Notice.chat("Can't use this command right now.")
+ var commandPasswordNotSetForAccountError: Notice =
+ Notice.chat("Can't execute this command right now: your account has no password set.")
+ var commandNoPremiumAccountWithUsername: Notice =
+ Notice.chat(
+ "Can't set this account as premium because there's no premium account with username '%USERNAME%'."
+ )
+ var accountAlreadyPremiumError: Notice =
+ Notice.chat("Account is already set as a premium one.")
+
+ var registerPasswordInvalidError: Notice =
+ Notice.chat(
+ "The password is invalid. It must be at least 5 characters long and contain at least one uppercase letter and one digit."
+ )
+ var registerPasswordsMustMatchError: Notice = Notice.chat("Both passwords must match.")
+
var loginPasswordOnlyInstruction: Notice =
Notice.chat("Please login using \"/login \" command.")
var loginTwoFactorOnlyInstruction: Notice =
@@ -86,11 +117,26 @@ open class MessagesConfig : OkaeriConfig() {
var loginSuccess: Notice =
Notice.chat("You have been authenticated successfully.")
-
var registerSuccess: Notice = Notice.chat("Successfully registered")
-
var premiumAuthSuccess: Notice = Notice.chat("Auto-logged in")
+ var accountMigrationSuccess: Notice = Notice.chat("Account migrated successfully!")
+ var newPasswordSetSuccess: Notice = Notice.chat("Success! New password set.")
+
+ var wrongCredentialsError: Notice = Notice.chat("Wrong credentials provided!")
+
+ var twoFactorDisabledSuccess: Notice = Notice.chat("2FA is now disabled!")
+ var twoFactorEnabledSuccess: Notice = Notice.chat("2FA is now enabled!")
+ var twoFactorCodeRequiredError: Notice =
+ Notice.chat("Please provide two-factor authentication code.")
+ var twoFactorSessionNotFound: Notice =
+ Notice.chat(
+ "2FA setup session not found. Please try again using /setup2fa command first."
+ )
+ var twoFactorWrongCodeError: Notice = Notice.chat("Wrong 2FA code!")
+ var twoFactorAlreadyDisabledError: Notice =
+ Notice.chat("Your account has 2FA disabled already.")
+
var twoFactorSetupInstruction: Notice =
Notice.chat(
"""
@@ -112,6 +158,39 @@ open class MessagesConfig : OkaeriConfig() {
"""
.trimIndent()
)
+
+ var adminCmdUsernameIsInvalid: Notice =
+ Notice.chat("Provided username '%USERNAME%' is invalid.")
+ var adminCmdAccountIsPremiumError: Notice =
+ Notice.chat("Can't execute the command! Account '%USERNAME%' is set to premium mode.")
+ var adminCmdAccountIsAlreadyNonPremiumError: Notice =
+ Notice.chat("Account '%USERNAME%' is already a non-premium account.")
+ var adminCmdUseForceCrackedFirst: Notice = Notice.chat("Use /forcecracked command first.")
+ var adminCmdCantMigrateToExistingPremiumAccount: Notice =
+ Notice.chat(
+ "Provided username '%USERNAME%' is found as Mojang premium profile. Can't migrate to premium account."
+ )
+ var adminCmdUsernameAlreadyTakenError: Notice =
+ Notice.chat(
+ "Username '%USERNAME%' is already taken. Please try again with a different username."
+ )
+ var adminCmdUsernameNotPremiumError: Notice =
+ Notice.chat(
+ "Can't find '%USERNAME%' user in Mojang database. This player can't be migrated to premium mode."
+ )
+
+ var adminCmdPasswordSetSuccess: Notice =
+ Notice.chat("Success! User '%USERNAME%' password was set.")
+ var adminCmdAccountMigratedToNonPremiumSuccess: Notice =
+ Notice.chat(
+ "User '%USERNAME%' has been successfully migrated to non-premium mode. Their new password is: %PASSWORD_TEXT%"
+ )
+ var adminCmdUserDataMigratedSuccess: Notice =
+ Notice.chat(
+ "Success! User '%OLD_USERNAME%' data has been migrated to '%NEW_USERNAME%'."
+ )
+ var adminCmdUserPremiumMigrationSuccess: Notice =
+ Notice.chat("User '%USERNAME%' successfully migrated to premium mode.")
}
@Variable("CONFIG_VERSION")
diff --git a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/config/MigrationConfig.kt b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/config/MigrationConfig.kt
index 9fa5c77..91142f4 100644
--- a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/config/MigrationConfig.kt
+++ b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/config/MigrationConfig.kt
@@ -27,7 +27,7 @@ import pl.spcode.navauth.common.migrate.MigrationOriginPluginType
@Header(
"Database migration config",
"This file contains database migration settings.",
- "Check https://navio1430.github.io/NavAuth/migration/migration.html for more information",
+ "Check https://navio1430.github.io/NavAuth/docs/migration/migration.html for more information",
)
class MigrationConfig : OkaeriConfig() {
diff --git a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/auth/LoginAuthSession.kt b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/auth/LoginAuthSession.kt
index 07b58f9..15d3879 100644
--- a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/auth/LoginAuthSession.kt
+++ b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/auth/LoginAuthSession.kt
@@ -42,6 +42,10 @@ open class LoginAuthSession(
override fun onInvalidate() {}
+ open fun onTooManyLoginAttempts() {
+ playerAdapter.disconnect(DisconnectReason.TOO_MANY_LOGIN_ATTEMPTS)
+ }
+
/**
* Authenticates a user using a combination of password and two-factor authentication code if
* required.
@@ -58,7 +62,7 @@ open class LoginAuthSession(
if (!result) {
attemptsLeft -= 1
if (attemptsLeft <= 0) {
- playerAdapter.disconnect(DisconnectReason.TOO_MANY_LOGIN_ATTEMPTS)
+ onTooManyLoginAttempts()
}
}
return result
diff --git a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/shared/data/OrmLiteCrudRepository.kt b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/OrmLiteCrudRepository.kt
similarity index 95%
rename from navauth-common/src/main/kotlin/pl/spcode/navauth/common/shared/data/OrmLiteCrudRepository.kt
rename to navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/OrmLiteCrudRepository.kt
index a2eaac7..86b6829 100644
--- a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/shared/data/OrmLiteCrudRepository.kt
+++ b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/OrmLiteCrudRepository.kt
@@ -16,7 +16,7 @@
*
*/
-package pl.spcode.navauth.common.shared.data
+package pl.spcode.navauth.common.infra.persistence.ormlite
import com.j256.ormlite.dao.Dao
diff --git a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/shared/data/OrmLiteCrudRepositoryImpl.kt b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/OrmLiteCrudRepositoryImpl.kt
similarity index 97%
rename from navauth-common/src/main/kotlin/pl/spcode/navauth/common/shared/data/OrmLiteCrudRepositoryImpl.kt
rename to navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/OrmLiteCrudRepositoryImpl.kt
index 2a601eb..6946afd 100644
--- a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/shared/data/OrmLiteCrudRepositoryImpl.kt
+++ b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/OrmLiteCrudRepositoryImpl.kt
@@ -16,7 +16,7 @@
*
*/
-package pl.spcode.navauth.common.shared.data
+package pl.spcode.navauth.common.infra.persistence.ormlite
import com.j256.ormlite.dao.Dao
import com.j256.ormlite.stmt.DeleteBuilder
diff --git a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/credentials/UserCredentialsRepositoryImpl.kt b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/credentials/UserCredentialsRepositoryImpl.kt
index 40e9809..8ba9e14 100644
--- a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/credentials/UserCredentialsRepositoryImpl.kt
+++ b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/credentials/UserCredentialsRepositoryImpl.kt
@@ -27,7 +27,7 @@ import pl.spcode.navauth.common.domain.user.User
import pl.spcode.navauth.common.infra.database.DatabaseManager
import pl.spcode.navauth.common.infra.persistence.mapper.toDomain
import pl.spcode.navauth.common.infra.persistence.mapper.toRecord
-import pl.spcode.navauth.common.shared.data.OrmLiteCrudRepositoryImpl
+import pl.spcode.navauth.common.infra.persistence.ormlite.OrmLiteCrudRepositoryImpl
class UserCredentialsRepositoryImpl @Inject constructor(databaseManager: DatabaseManager) :
OrmLiteCrudRepositoryImpl(
diff --git a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/user/UserActivitySessionRepositoryImpl.kt b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/user/UserActivitySessionRepositoryImpl.kt
index 571315f..bb48545 100644
--- a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/user/UserActivitySessionRepositoryImpl.kt
+++ b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/user/UserActivitySessionRepositoryImpl.kt
@@ -27,7 +27,7 @@ import pl.spcode.navauth.common.infra.database.DatabaseManager
import pl.spcode.navauth.common.infra.persistence.Paginator
import pl.spcode.navauth.common.infra.persistence.mapper.toDomain
import pl.spcode.navauth.common.infra.persistence.mapper.toRecord
-import pl.spcode.navauth.common.shared.data.OrmLiteCrudRepositoryImpl
+import pl.spcode.navauth.common.infra.persistence.ormlite.OrmLiteCrudRepositoryImpl
class UserActivitySessionRepositoryImpl @Inject constructor(databaseManager: DatabaseManager) :
OrmLiteCrudRepositoryImpl(
diff --git a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/user/UserRepositoryImpl.kt b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/user/UserRepositoryImpl.kt
index 8918b7b..156f8da 100644
--- a/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/user/UserRepositoryImpl.kt
+++ b/navauth-common/src/main/kotlin/pl/spcode/navauth/common/infra/persistence/ormlite/user/UserRepositoryImpl.kt
@@ -28,7 +28,7 @@ import pl.spcode.navauth.common.domain.user.UserUuid
import pl.spcode.navauth.common.infra.database.DatabaseManager
import pl.spcode.navauth.common.infra.persistence.mapper.toDomain
import pl.spcode.navauth.common.infra.persistence.mapper.toRecord
-import pl.spcode.navauth.common.shared.data.OrmLiteCrudRepositoryImpl
+import pl.spcode.navauth.common.infra.persistence.ormlite.OrmLiteCrudRepositoryImpl
class UserRepositoryImpl @Inject constructor(databaseManager: DatabaseManager) :
OrmLiteCrudRepositoryImpl(databaseManager, UserRecord::class), UserRepository {
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/ForceChangePasswordAdminCommand.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/ForceChangePasswordAdminCommand.kt
index 3200601..6b3c7f7 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/ForceChangePasswordAdminCommand.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/ForceChangePasswordAdminCommand.kt
@@ -26,14 +26,13 @@ import dev.rollczi.litecommands.annotations.command.Command
import dev.rollczi.litecommands.annotations.context.Context
import dev.rollczi.litecommands.annotations.execute.Execute
import dev.rollczi.litecommands.annotations.permission.Permission
-import net.kyori.adventure.text.Component
import pl.spcode.navauth.common.annotation.Description
import pl.spcode.navauth.common.application.credentials.UserCredentialsService
import pl.spcode.navauth.common.application.user.UserService
import pl.spcode.navauth.common.command.user.UserArgumentResolver
import pl.spcode.navauth.common.command.user.UsernameOrUuidRaw
-import pl.spcode.navauth.common.component.TextColors
import pl.spcode.navauth.velocity.command.Permissions
+import pl.spcode.navauth.velocity.multification.VelocityMultification
@Command(name = "forcesetpassword")
@Permission(Permissions.ADMIN_FORCE_SET_PASSWORD)
@@ -43,6 +42,7 @@ constructor(
val userService: UserService,
val userCredentialsService: UserCredentialsService,
val userArgumentResolver: UserArgumentResolver,
+ val multification: VelocityMultification,
) {
@Async
@@ -58,18 +58,17 @@ constructor(
val user = userArgumentResolver.resolve(usernameOrUuidRaw)
if (user.isPremium) {
- sender.sendMessage(
- Component.text(
- "Can't execute the command! Account '${user.username}' is set to premium mode.",
- TextColors.RED,
- )
- )
+ multification
+ .create(sender) { it.multification.adminCmdAccountIsPremiumError }
+ .placeholder("%USERNAME%", user.username.value)
+ .send()
return
}
userCredentialsService.updatePassword(user, password)
- sender.sendMessage(
- Component.text("Success! User '${user.username}' credentials set.", TextColors.GREEN)
- )
+ multification
+ .create(sender) { it.multification.adminCmdPasswordSetSuccess }
+ .placeholder("%USERNAME%", user.username.value)
+ .send()
}
}
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/ForceCrackedAdminCommand.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/ForceCrackedAdminCommand.kt
index 312670e..369c084 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/ForceCrackedAdminCommand.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/ForceCrackedAdminCommand.kt
@@ -29,16 +29,15 @@ import dev.rollczi.litecommands.annotations.execute.Execute
import dev.rollczi.litecommands.annotations.permission.Permission
import java.util.Optional
import me.uniodex.velocityrcon.commandsource.IRconCommandSource
-import net.kyori.adventure.text.Component
-import net.kyori.adventure.text.minimessage.MiniMessage
import pl.spcode.navauth.common.annotation.Description
import pl.spcode.navauth.common.application.credentials.UserCredentialsService
import pl.spcode.navauth.common.application.user.UserService
import pl.spcode.navauth.common.command.user.UserArgumentResolver
import pl.spcode.navauth.common.command.user.UsernameOrUuidRaw
-import pl.spcode.navauth.common.component.TextColors
+import pl.spcode.navauth.common.extension.StringExtensions.Companion.applyPlaceholders
import pl.spcode.navauth.common.shared.utils.StringUtils.Companion.generateRandomString
import pl.spcode.navauth.velocity.command.Permissions
+import pl.spcode.navauth.velocity.multification.VelocityMultification
@Command(name = "forcecracked")
@Permission(Permissions.ADMIN_FORCE_CRACKED)
@@ -48,6 +47,7 @@ constructor(
val userService: UserService,
val userArgumentResolver: UserArgumentResolver,
val userCredentialsService: UserCredentialsService,
+ val multification: VelocityMultification,
) {
@Async
@@ -64,9 +64,10 @@ constructor(
val user = userArgumentResolver.resolve(usernameOrUuidRaw)
if (!user.isPremium) {
- sender.sendMessage(
- Component.text("User '${user.username}' is already non-premium account.", TextColors.RED)
- )
+ multification
+ .create(sender) { it.multification.adminCmdAccountIsAlreadyNonPremiumError }
+ .placeholder("%USERNAME%", user.username.value)
+ .send()
return
}
@@ -79,13 +80,14 @@ constructor(
if (sender is ConsoleCommandSource || sender is IRconCommandSource) {
"$newPassword"
} else {
- "CLICK HERE TO COPY"
+ val placeholders = mapOf(Pair("PASSWORD", newPassword))
+ multification.config.adminCopyPasswordText.applyPlaceholders(placeholders)
}
- sender.sendMessage(
- MiniMessage.miniMessage()
- .deserialize(
- "<${TextColors.GREEN.asHexString()}>User '${user.username}' has been successfully migrated to non-premium mode. Their new password is: $passwordText"
- )
- )
+
+ multification
+ .create(sender) { it.multification.adminCmdAccountMigratedToNonPremiumSuccess }
+ .placeholder("%USERNAME%", user.username.value)
+ .placeholder("%PASSWORD_TEXT%", passwordText)
+ .send()
}
}
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/ForcePremiumAdminCommand.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/ForcePremiumAdminCommand.kt
index 39ecc3b..9edb997 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/ForcePremiumAdminCommand.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/ForcePremiumAdminCommand.kt
@@ -26,14 +26,13 @@ import dev.rollczi.litecommands.annotations.command.Command
import dev.rollczi.litecommands.annotations.context.Context
import dev.rollczi.litecommands.annotations.execute.Execute
import dev.rollczi.litecommands.annotations.permission.Permission
-import net.kyori.adventure.text.Component
import pl.spcode.navauth.common.annotation.Description
import pl.spcode.navauth.common.application.mojang.MojangProfileService
import pl.spcode.navauth.common.application.user.UserService
import pl.spcode.navauth.common.command.user.UserArgumentResolver
import pl.spcode.navauth.common.command.user.UsernameOrUuidRaw
-import pl.spcode.navauth.common.component.TextColors
import pl.spcode.navauth.velocity.command.Permissions
+import pl.spcode.navauth.velocity.multification.VelocityMultification
@Command(name = "forcepremium")
@Permission(Permissions.ADMIN_FORCE_PREMIUM)
@@ -43,6 +42,7 @@ constructor(
val userService: UserService,
val profileService: MojangProfileService,
val userArgumentResolver: UserArgumentResolver,
+ val multification: VelocityMultification,
) {
@Execute
@@ -55,27 +55,26 @@ constructor(
val user = userArgumentResolver.resolve(usernameOrUuidRaw)
if (user.isPremium) {
- sender.sendMessage(Component.text("User is already premium.", TextColors.RED))
+ multification
+ .create(sender) { it.multification.adminCmdAccountIsPremiumError }
+ .placeholder("%USERNAME%", user.username.value)
+ .send()
return
}
val profile = profileService.fetchProfileInfo(user.username)
if (profile == null) {
- sender.sendMessage(
- Component.text(
- "Can't find '${user.username}' user in Mojang database. This player can't be migrated to premium mode.",
- TextColors.RED,
- )
- )
+ multification
+ .create(sender) { it.multification.adminCmdUsernameNotPremiumError }
+ .placeholder("%USERNAME%", user.username.value)
+ .send()
return
}
userService.migrateToPremium(user, profile.uuid)
- sender.sendMessage(
- Component.text(
- "User '${user.username}' successfully migrated to premium mode.",
- TextColors.GREEN,
- )
- )
+ multification
+ .create(sender) { it.multification.adminCmdUserPremiumMigrationSuccess }
+ .placeholder("%USERNAME%", user.username.value)
+ .send()
}
}
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/MigrateUserDataAdminCommand.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/MigrateUserDataAdminCommand.kt
index 2de059b..ef0ab45 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/MigrateUserDataAdminCommand.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/admin/MigrateUserDataAdminCommand.kt
@@ -27,7 +27,6 @@ import dev.rollczi.litecommands.annotations.command.Command
import dev.rollczi.litecommands.annotations.context.Context
import dev.rollczi.litecommands.annotations.execute.Execute
import dev.rollczi.litecommands.annotations.permission.Permission
-import net.kyori.adventure.text.Component
import pl.spcode.navauth.common.annotation.Description
import pl.spcode.navauth.common.application.mojang.MojangProfileService
import pl.spcode.navauth.common.application.user.UserService
@@ -35,9 +34,9 @@ import pl.spcode.navauth.common.application.user.UsernameAlreadyTakenException
import pl.spcode.navauth.common.application.validator.UsernameValidator
import pl.spcode.navauth.common.command.user.UserArgumentResolver
import pl.spcode.navauth.common.command.user.UsernameOrUuidRaw
-import pl.spcode.navauth.common.component.TextColors
import pl.spcode.navauth.common.domain.user.Username
import pl.spcode.navauth.velocity.command.Permissions
+import pl.spcode.navauth.velocity.multification.VelocityMultification
@Command(name = "migrateuser")
@Permission(Permissions.ADMIN_MIGRATE_USER_DATA)
@@ -49,6 +48,7 @@ constructor(
val profileService: MojangProfileService,
val userArgumentResolver: UserArgumentResolver,
val usernameValidator: UsernameValidator,
+ val multification: VelocityMultification,
) {
@Async
@@ -67,56 +67,54 @@ constructor(
val user = userArgumentResolver.resolve(usernameOrUuidRaw)
if (user.isPremium) {
- sender.sendMessage(
- Component.text(
- "Can't execute the command! Account '${user.username}' is set to premium mode. Use /forcecracked command first.",
- TextColors.RED,
- )
- )
+ multification
+ .create(sender) { it.multification.adminCmdAccountIsPremiumError }
+ .placeholder("%USERNAME%", user.username.value)
+ .send()
+ multification.send(sender) { it.multification.adminCmdUseForceCrackedFirst }
return
}
if (!usernameValidator.isValid(newUsername)) {
- sender.sendMessage(
- Component.text("Provided username '${newUsername}' is invalid.", TextColors.RED)
- )
+ multification
+ .create(sender) { it.multification.adminCmdUsernameIsInvalid }
+ .placeholder("%USERNAME%", newUsername)
+ .send()
return
}
val premiumMojangProfile = profileService.fetchProfileInfo(Username(newUsername))
if (premiumMojangProfile != null) {
- sender.sendMessage(
- Component.text(
- "Provided username '${user.username}' is found as Mojang premium profile. Can't migrate to premium account.",
- TextColors.RED,
- )
- )
+ multification
+ .create(sender) { it.multification.adminCmdCantMigrateToExistingPremiumAccount }
+ .placeholder("%USERNAME%", user.username.value)
+ .send()
return
}
try {
userService.migrateData(user, Username(newUsername))
} catch (e: UsernameAlreadyTakenException) {
- sender.sendMessage(
- Component.text(
- "Username '${newUsername}' is already taken. Please try again with different username.",
- TextColors.RED,
- )
- )
+ multification
+ .create(sender) { it.multification.adminCmdUsernameAlreadyTakenError }
+ .placeholder("%USERNAME%", newUsername)
+ .send()
return
}
proxyServer.getPlayer(user.username.value).ifPresent {
- it.disconnect(
- Component.text("Your account data has been migrated to '${newUsername}'.", TextColors.GREEN)
- )
+ val disconnectReason =
+ multification.config.yourAccountDataHasBeenMigrated
+ .withPlaceholders()
+ .placeholder("USERNAME", newUsername)
+ .toComponent()
+ it.disconnect(disconnectReason)
}
- sender.sendMessage(
- Component.text(
- "Success! User '${user.username}' data has been migrated to '${newUsername}'.",
- TextColors.GREEN,
- )
- )
+ multification
+ .create(sender) { it.multification.adminCmdUserDataMigratedSuccess }
+ .placeholder("%OLD_USERNAME%", user.username.value)
+ .placeholder("%NEW_USERNAME%", newUsername)
+ .send()
}
}
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/ChangePasswordCommand.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/ChangePasswordCommand.kt
index f5cab2b..a9fde53 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/ChangePasswordCommand.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/ChangePasswordCommand.kt
@@ -26,18 +26,21 @@ import dev.rollczi.litecommands.annotations.command.Command
import dev.rollczi.litecommands.annotations.context.Context
import dev.rollczi.litecommands.annotations.execute.Execute
import dev.rollczi.litecommands.annotations.permission.Permission
-import net.kyori.adventure.text.Component
import pl.spcode.navauth.common.annotation.Description
import pl.spcode.navauth.common.application.credentials.UserCredentialsService
import pl.spcode.navauth.common.application.user.UserService
-import pl.spcode.navauth.common.component.TextColors
import pl.spcode.navauth.velocity.command.Permissions
+import pl.spcode.navauth.velocity.multification.VelocityMultification
@Command(name = "changepassword")
@Permission(Permissions.USER_CHANGE_PASSWORD)
class ChangePasswordCommand
@Inject
-constructor(val userService: UserService, val userCredentialsService: UserCredentialsService) {
+constructor(
+ val userService: UserService,
+ val userCredentialsService: UserCredentialsService,
+ val multification: VelocityMultification,
+) {
@Async
@Execute
@@ -48,23 +51,20 @@ constructor(val userService: UserService, val userCredentialsService: UserCreden
@Arg(value = "new_password") newPassword: String,
) {
val user = userService.findUserByExactUsername(sender.username)!!
- if (user.isPremium) {
- sender.sendMessage(
- Component.text(
- "Can't execute this command right now: your account is set to premium mode.",
- TextColors.RED,
- )
- )
+ val credentials = userCredentialsService.findCredentials(user)!!
+
+ if (!credentials.isPasswordRequired) {
+ multification.send(sender) { it.multification.commandPasswordNotSetForAccountError }
return
}
- val credentials = userCredentialsService.findCredentials(user)!!
+
val isCorrectPassword = userCredentialsService.verifyPassword(credentials, currentPassword)
if (!isCorrectPassword) {
- sender.sendMessage(Component.text("Wrong password!", TextColors.RED))
+ multification.send(sender) { it.multification.wrongCredentialsError }
return
}
userCredentialsService.updatePassword(user, newPassword)
- sender.sendMessage(Component.text("Success! New password set.", TextColors.GREEN))
+ multification.send(sender) { it.multification.newPasswordSetSuccess }
}
}
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/LoginCommand.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/LoginCommand.kt
index b704303..7fa402c 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/LoginCommand.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/LoginCommand.kt
@@ -28,22 +28,24 @@ import dev.rollczi.litecommands.annotations.context.Context
import dev.rollczi.litecommands.annotations.execute.Execute
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
-import net.kyori.adventure.text.Component
import pl.spcode.navauth.api.domain.auth.AuthSessionType
import pl.spcode.navauth.common.annotation.Description
import pl.spcode.navauth.common.application.auth.session.AuthSessionService
import pl.spcode.navauth.common.command.exception.MissingPermissionException
-import pl.spcode.navauth.common.component.TextColors
import pl.spcode.navauth.velocity.command.Permissions
import pl.spcode.navauth.velocity.infra.auth.VelocityLoginAuthSession
import pl.spcode.navauth.velocity.infra.auth.VelocityUniqueSessionId
import pl.spcode.navauth.velocity.infra.player.VelocityPlayerAdapter
+import pl.spcode.navauth.velocity.multification.VelocityMultification
// we use inverted permission in this command
@RootCommand
class LoginCommand
@Inject
-constructor(val authSessionService: AuthSessionService) {
+constructor(
+ val authSessionService: AuthSessionService,
+ val multification: VelocityMultification,
+) {
@Async
@Execute(name = "login")
@@ -66,7 +68,7 @@ constructor(val authSessionService: AuthSessionService) {
val uniqueSessionId = VelocityUniqueSessionId(sender)
val session = authSessionService.findSession(uniqueSessionId)
if (session?.getSessionType() != AuthSessionType.LOGIN || session.isAuthenticated) {
- sender.sendMessage(Component.text("Can't use this command right now.", TextColors.RED))
+ multification.send(sender) { it.multification.cantUseThisCommandNowError }
return
}
@@ -74,9 +76,7 @@ constructor(val authSessionService: AuthSessionService) {
if (session.userCredentials.isTwoFactorEnabled) {
if (!twoFactorCode.isPresent) {
- sender.sendMessage(
- Component.text("Please provide two-factor authentication code.", TextColors.RED)
- )
+ multification.send(sender) { it.multification.twoFactorCodeRequiredError }
return
}
}
@@ -101,16 +101,14 @@ constructor(val authSessionService: AuthSessionService) {
val uniqueSessionId = VelocityUniqueSessionId(sender)
val session = authSessionService.findSession(uniqueSessionId)
if (session?.getSessionType() != AuthSessionType.LOGIN || session.isAuthenticated) {
- sender.sendMessage(Component.text("Can't use this command right now.", TextColors.RED))
+ multification.send(sender) { it.multification.cantUseThisCommandNowError }
return
}
session as VelocityLoginAuthSession
if (!session.userCredentials.isTwoFactorEnabled || session.userCredentials.isPasswordRequired) {
- sender.sendMessage(
- Component.text("Can't use 2fa command right now. Please use /login command.")
- )
+ multification.send(sender) { it.multification.cantUseThisCommandNowError }
return
}
@@ -124,10 +122,9 @@ constructor(val authSessionService: AuthSessionService) {
code: String?,
) {
if (session.auth(password, code)) {
- sender.sendMessage(Component.text("Logged in!", TextColors.GREEN))
return
}
- sender.sendMessage(Component.text("Wrong credentials!", TextColors.RED))
+ multification.send(sender) { it.multification.wrongCredentialsError }
}
}
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/PremiumAccountCommand.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/PremiumAccountCommand.kt
index 82f67d7..46edf00 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/PremiumAccountCommand.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/PremiumAccountCommand.kt
@@ -25,20 +25,23 @@ import dev.rollczi.litecommands.annotations.async.Async
import dev.rollczi.litecommands.annotations.command.Command
import dev.rollczi.litecommands.annotations.context.Context
import dev.rollczi.litecommands.annotations.execute.Execute
-import net.kyori.adventure.text.Component
import pl.spcode.navauth.common.annotation.Description
import pl.spcode.navauth.common.application.mojang.MojangProfileService
import pl.spcode.navauth.common.application.user.UserService
import pl.spcode.navauth.common.command.exception.MissingPermissionException
-import pl.spcode.navauth.common.component.TextColors
import pl.spcode.navauth.common.domain.user.Username
import pl.spcode.navauth.velocity.command.Permissions
+import pl.spcode.navauth.velocity.multification.VelocityMultification
// inverted permission
@Command(name = "premium")
class PremiumAccountCommand
@Inject
-constructor(val userService: UserService, val mojangProfileService: MojangProfileService) {
+constructor(
+ val userService: UserService,
+ val mojangProfileService: MojangProfileService,
+ val multification: VelocityMultification,
+) {
@Async
@Execute
@@ -56,22 +59,20 @@ constructor(val userService: UserService, val mojangProfileService: MojangProfil
val user = userService.findUserByExactUsername(sender.username)!!
if (user.isPremium) {
- sender.sendMessage(Component.text("Account is already set as a premium one.", TextColors.RED))
+ multification.send(sender) { it.multification.accountAlreadyPremiumError }
return
}
val mojangProfile = mojangProfileService.fetchProfileInfo(Username(sender.username))
if (mojangProfile == null) {
- sender.sendMessage(
- Component.text(
- "Can't set this account as premium because there's no premium account with username '${sender.username}'.",
- TextColors.RED,
- )
- )
+ multification
+ .create(sender) { it.multification.commandNoPremiumAccountWithUsername }
+ .placeholder("%USERNAME%", sender.username)
+ .send()
return
}
userService.migrateToPremium(user, mojangProfile.uuid)
- sender.sendMessage(Component.text("Account migrated successfully!", TextColors.GREEN))
+ multification.create(sender) { it.multification.accountMigrationSuccess }
}
}
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/RegisterCommand.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/RegisterCommand.kt
index 6b278d8..0760f16 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/RegisterCommand.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/RegisterCommand.kt
@@ -26,15 +26,12 @@ import dev.rollczi.litecommands.annotations.async.Async
import dev.rollczi.litecommands.annotations.command.Command
import dev.rollczi.litecommands.annotations.context.Context
import dev.rollczi.litecommands.annotations.execute.Execute
-import net.kyori.adventure.text.Component
-import net.kyori.adventure.text.format.TextColor
import pl.spcode.navauth.api.domain.auth.AuthSessionType
import pl.spcode.navauth.common.annotation.Description
import pl.spcode.navauth.common.application.auth.session.AuthSessionService
import pl.spcode.navauth.common.application.user.UserService
import pl.spcode.navauth.common.application.validator.PasswordValidator
import pl.spcode.navauth.common.command.exception.MissingPermissionException
-import pl.spcode.navauth.common.component.TextColors
import pl.spcode.navauth.common.domain.user.User
import pl.spcode.navauth.common.domain.user.UserUuid
import pl.spcode.navauth.common.domain.user.Username
@@ -42,6 +39,7 @@ import pl.spcode.navauth.common.infra.crypto.hasher.BCryptCredentialsHasher
import pl.spcode.navauth.velocity.command.Permissions
import pl.spcode.navauth.velocity.infra.auth.VelocityUniqueSessionId
import pl.spcode.navauth.velocity.infra.player.VelocityPlayerAdapter
+import pl.spcode.navauth.velocity.multification.VelocityMultification
// we use inverted permission in this command
@Command(name = "register")
@@ -51,6 +49,7 @@ constructor(
val authSessionService: AuthSessionService,
val userService: UserService,
val passwordValidator: PasswordValidator,
+ val multification: VelocityMultification,
) {
@Async
@@ -73,17 +72,17 @@ constructor(
val session = authSessionService.findSession(VelocityUniqueSessionId(sender))
if (session?.getSessionType() != AuthSessionType.REGISTER || session.isAuthenticated) {
- sender.sendMessage(Component.text("Can't use this command right now.", TextColors.RED))
+ multification.send(sender) { it.multification.cantUseThisCommandNowError }
return
}
if (!passwordValidator.isValid(password)) {
- sender.sendMessage(Component.text("Password is invalid.", TextColors.RED))
+ multification.send(sender) { it.multification.registerPasswordInvalidError }
return
}
if (password != repeatPassword) {
- sender.sendMessage(Component.text("Both passwords must match.", TextColors.RED))
+ multification.send(sender) { it.multification.registerPasswordsMustMatchError }
return
}
@@ -91,8 +90,7 @@ constructor(
User.nonPremium(UserUuid(sender.uniqueId), Username(sender.username)),
BCryptCredentialsHasher().hash(password),
)
+ // register session will send success message
session.authenticate()
-
- sender.sendMessage(Component.text("Account created", TextColor.color(0, 200, 0)))
}
}
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/TwoFactorDisableCommand.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/TwoFactorDisableCommand.kt
index 8eea233..c51627f 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/TwoFactorDisableCommand.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/TwoFactorDisableCommand.kt
@@ -26,20 +26,23 @@ import dev.rollczi.litecommands.annotations.async.Async
import dev.rollczi.litecommands.annotations.command.RootCommand
import dev.rollczi.litecommands.annotations.context.Context
import dev.rollczi.litecommands.annotations.execute.Execute
-import net.kyori.adventure.text.Component
import pl.spcode.navauth.common.annotation.Description
import pl.spcode.navauth.common.application.credentials.UserCredentialsService
import pl.spcode.navauth.common.application.user.UserService
import pl.spcode.navauth.common.command.exception.MissingPermissionException
-import pl.spcode.navauth.common.component.TextColors
import pl.spcode.navauth.common.domain.user.UserUuid
import pl.spcode.navauth.velocity.command.Permissions
+import pl.spcode.navauth.velocity.multification.VelocityMultification
// we use inverted permission in this command
@RootCommand
class TwoFactorDisableCommand
@Inject
-constructor(val userService: UserService, val userCredentialsService: UserCredentialsService) {
+constructor(
+ val userService: UserService,
+ val userCredentialsService: UserCredentialsService,
+ val multification: VelocityMultification,
+) {
@Async
@Execute(name = "disable2fa")
@@ -53,17 +56,17 @@ constructor(val userService: UserService, val userCredentialsService: UserCreden
val user = userService.findUserByUuid(UserUuid(sender.uniqueId))!!
val credentials = userCredentialsService.findCredentials(user)
if (credentials == null || !credentials.isTwoFactorEnabled) {
- sender.sendMessage(Component.text("Your account has 2FA disabled already.", TextColors.RED))
+ multification.send(sender) { it.multification.twoFactorAlreadyDisabledError }
return
}
if (!userCredentialsService.verifyCode(credentials, code)) {
- sender.sendMessage(Component.text("Wrong code!", TextColors.RED))
+ multification.send(sender) { it.multification.twoFactorWrongCodeError }
return
}
userService.disableTwoFactorAuth(user)
- sender.sendMessage(Component.text("2FA disabled!", TextColors.GREEN))
+ multification.send(sender) { it.multification.twoFactorDisabledSuccess }
return
}
}
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/TwoFactorSetupCommand.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/TwoFactorSetupCommand.kt
index 2845ce5..8aa7f6f 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/TwoFactorSetupCommand.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/command/user/TwoFactorSetupCommand.kt
@@ -27,13 +27,11 @@ import dev.rollczi.litecommands.annotations.command.RootCommand
import dev.rollczi.litecommands.annotations.context.Context
import dev.rollczi.litecommands.annotations.execute.Execute
import java.util.Optional
-import net.kyori.adventure.text.Component
import net.kyori.adventure.text.minimessage.MiniMessage
import pl.spcode.navauth.common.annotation.Description
import pl.spcode.navauth.common.application.credentials.UserCredentialsService
import pl.spcode.navauth.common.application.user.UserService
import pl.spcode.navauth.common.command.exception.MissingPermissionException
-import pl.spcode.navauth.common.component.TextColors
import pl.spcode.navauth.common.config.MessagesConfig
import pl.spcode.navauth.common.config.TwoFactorAuthConfig
import pl.spcode.navauth.common.domain.user.UserUuid
@@ -57,7 +55,7 @@ constructor(
val totpSetupSessionService: TotpSetupSessionService,
val twoFactorAuthConfig: TwoFactorAuthConfig,
val messages: MessagesConfig,
- val velocityMultification: VelocityMultification,
+ val multification: VelocityMultification,
) {
@Async
@@ -75,25 +73,20 @@ constructor(
val user = userService.findUserByUuid(UserUuid(sender.uniqueId))!!
val credentials = userCredentialsService.findCredentials(user)
if (credentials?.isTwoFactorEnabled == true) {
- sender.sendMessage(
- Component.text(
- "Can't execute this command right now: your account has 2FA enabled already.",
- TextColors.RED,
- )
- )
+ multification.send(sender) { it.multification.twoFactorAlreadyEnabledError }
return
}
if (credentials?.isPasswordRequired == true) {
if (currentPassword.isEmpty) {
- sender.sendMessage(Component.text("Please provide your current password.", TextColors.RED))
+ multification.send(sender) { it.multification.passwordRequiredError }
return
}
val isCorrectPassword =
userCredentialsService.verifyPassword(credentials, currentPassword.get())
if (!isCorrectPassword) {
- sender.sendMessage(Component.text("Wrong password!", TextColors.RED))
+ multification.send(sender) { it.multification.wrongCredentialsError }
return
}
}
@@ -103,7 +96,7 @@ constructor(
totpSetupSessionService.registerSession(UserUuid(sender.uniqueId), session)
val remainingSeconds = TotpSetupSessionService.SESSION_LIFETIME_SECONDS
- velocityMultification
+ multification
.create()
.player(sender.uniqueId)
.notice(messages.multification.twoFactorSetupInstruction)
@@ -121,29 +114,24 @@ constructor(
val user = userService.findUserByUuid(UserUuid(sender.uniqueId))!!
if (userCredentialsService.findCredentials(user)?.isTwoFactorEnabled == true) {
- sender.sendMessage(Component.text("2FA is already enabled for this account.", TextColors.RED))
+ multification.send(sender) { it.multification.twoFactorAlreadyEnabledError }
return
}
val session = totpSetupSessionService.findSession(UserUuid(sender.uniqueId))
if (session == null) {
- sender.sendMessage(
- Component.text(
- "2FA setup session not found. Please try again using /setup2fa command first.",
- TextColors.RED,
- )
- )
+ multification.send(sender) { it.multification.twoFactorSessionNotFound }
return
}
if (!TOTP2FA().verifyTOTP(session.secret, code)) {
- sender.sendMessage(Component.text("Invalid 2FA code!", TextColors.RED))
+ multification.send(sender) { it.multification.twoFactorWrongCodeError }
return
}
userService.enableTwoFactorAuth(user, session.secret)
totpSetupSessionService.closeSession(UserUuid(sender.uniqueId))
- sender.sendMessage(Component.text("2FA successfully enabled!", TextColors.GREEN))
+ multification.send(sender) { it.multification.twoFactorEnabledSuccess }
}
@Execute(name = "generate2faqr")
@@ -154,12 +142,7 @@ constructor(
fun generateQrCode(@Context sender: Player) {
val session = totpSetupSessionService.findSession(UserUuid(sender.uniqueId))
if (session == null) {
- sender.sendMessage(
- Component.text(
- "2FA setup session not found. Please try again using /setup2fa command first.",
- TextColors.RED,
- )
- )
+ multification.send(sender) { it.multification.twoFactorSessionNotFound }
return
}
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/extension/PlayerDisconnectExtension.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/extension/PlayerDisconnectExtension.kt
new file mode 100644
index 0000000..7285797
--- /dev/null
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/extension/PlayerDisconnectExtension.kt
@@ -0,0 +1,32 @@
+/*
+ * NavAuth
+ * Copyright © 2026 Oliwier Fijas (Navio1430)
+ *
+ * NavAuth is free software; You can redistribute it and/or modify it under the terms of:
+ * the GNU Affero General Public License version 3 as published by the Free Software Foundation.
+ *
+ * NavAuth is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with NavAuth. If not, see
+ * and navigate to version 3 of the GNU Affero General Public License.
+ *
+ */
+
+package pl.spcode.navauth.velocity.extension
+
+import com.velocitypowered.api.proxy.Player
+import net.kyori.adventure.text.Component
+
+class PlayerDisconnectExtension {
+ companion object {
+ fun Player.disconnectIfActive(reason: Component) {
+ if (this.isActive) {
+ this.disconnect(reason)
+ }
+ }
+ }
+}
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/auth/VelocityAutoLoginAuthSession.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/auth/VelocityAutoLoginAuthSession.kt
index f514147..b7d55ae 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/auth/VelocityAutoLoginAuthSession.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/auth/VelocityAutoLoginAuthSession.kt
@@ -46,11 +46,13 @@ class VelocityAutoLoginAuthSession(
scheduler
.buildTask(
Runnable {
- multification
- .create()
- .player(player.uniqueId)
- .notice(messagesConfig.multification.premiumAuthSuccess)
- .send()
+ if (player.isActive) {
+ multification
+ .create()
+ .player(player.uniqueId)
+ .notice(messagesConfig.multification.premiumAuthSuccess)
+ .send()
+ }
}
)
.delay(Duration.ofSeconds(1))
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/auth/VelocityLoginAuthSession.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/auth/VelocityLoginAuthSession.kt
index 7faa4d3..d444e3b 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/auth/VelocityLoginAuthSession.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/auth/VelocityLoginAuthSession.kt
@@ -28,6 +28,7 @@ import pl.spcode.navauth.common.config.MessagesConfig
import pl.spcode.navauth.common.domain.credentials.UserCredentials
import pl.spcode.navauth.common.infra.auth.LoginAuthSession
import pl.spcode.navauth.velocity.application.event.VelocityEventDispatcher
+import pl.spcode.navauth.velocity.extension.PlayerDisconnectExtension.Companion.disconnectIfActive
import pl.spcode.navauth.velocity.infra.player.VelocityPlayerAdapter
import pl.spcode.navauth.velocity.multification.VelocityMultification
import pl.spcode.navauth.velocity.scheduler.NavAuthScheduler
@@ -58,7 +59,9 @@ class VelocityLoginAuthSession(
disconnectPlayerTask =
scheduler
.buildTask(
- Runnable { player.disconnect(messagesConfig.loginTimeExceededError.toComponent()) }
+ Runnable {
+ player.disconnectIfActive(messagesConfig.loginTimeExceededError.toComponent())
+ }
)
.delay(generalConfig.maxLoginDuration)
.schedule()
@@ -96,6 +99,10 @@ class VelocityLoginAuthSession(
cancelTasks()
}
+ override fun onTooManyLoginAttempts() {
+ player.disconnectIfActive(messagesConfig.loginTooManyAttemptsError.toComponent())
+ }
+
fun cancelTasks() {
notifyMessageTask.cancel()
disconnectPlayerTask.cancel()
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/auth/VelocityRegisterAuthSession.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/auth/VelocityRegisterAuthSession.kt
index 969d8ed..732795c 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/auth/VelocityRegisterAuthSession.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/auth/VelocityRegisterAuthSession.kt
@@ -26,6 +26,7 @@ import pl.spcode.navauth.common.config.GeneralConfig
import pl.spcode.navauth.common.config.MessagesConfig
import pl.spcode.navauth.common.infra.auth.RegisterAuthSession
import pl.spcode.navauth.velocity.application.event.VelocityEventDispatcher
+import pl.spcode.navauth.velocity.extension.PlayerDisconnectExtension.Companion.disconnectIfActive
import pl.spcode.navauth.velocity.infra.player.VelocityPlayerAdapter
import pl.spcode.navauth.velocity.multification.VelocityMultification
import pl.spcode.navauth.velocity.scheduler.NavAuthScheduler
@@ -47,7 +48,9 @@ class VelocityRegisterAuthSession(
disconnectPlayerTask =
scheduler
.buildTask(
- Runnable { player.disconnect(messagesConfig.registerTimeExceededError.toComponent()) }
+ Runnable {
+ player.disconnectIfActive(messagesConfig.registerTimeExceededError.toComponent())
+ }
)
.delay(generalConfig.maxRegistrationDuration)
.schedule()
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/player/VelocityPlayerAdapter.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/player/VelocityPlayerAdapter.kt
index 1b62a25..64921f1 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/player/VelocityPlayerAdapter.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/infra/player/VelocityPlayerAdapter.kt
@@ -37,6 +37,9 @@ class VelocityPlayerAdapter(val velocityPlayer: Player) : PlayerAdapter {
}
override fun disconnect(reason: DisconnectReason) {
+ if (!isOnline()) {
+ return
+ }
when (reason) {
DisconnectReason.AUTH_SESSION_CLOSED ->
velocityPlayer.disconnect(
@@ -46,8 +49,12 @@ class VelocityPlayerAdapter(val velocityPlayer: Player) : PlayerAdapter {
)
)
DisconnectReason.TOO_MANY_LOGIN_ATTEMPTS ->
+ // this is handled by VelocityLoginAuthSession itself
velocityPlayer.disconnect(
- Component.text("Too many login attempts. Please try again later.", TextColors.RED)
+ Component.text(
+ "NavAuth: Too many login attempts. Please try again later.",
+ TextColors.RED,
+ )
)
}
}
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/listener/velocity/ConnectionListeners.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/listener/velocity/ConnectionListeners.kt
index 298a674..a5714e4 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/listener/velocity/ConnectionListeners.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/listener/velocity/ConnectionListeners.kt
@@ -35,6 +35,7 @@ import pl.spcode.navauth.common.config.GeneralConfig
import pl.spcode.navauth.common.domain.auth.session.AuthSession
import pl.spcode.navauth.common.domain.auth.session.AuthSessionState
import pl.spcode.navauth.velocity.application.server.VelocityServerSelectionService
+import pl.spcode.navauth.velocity.extension.PlayerDisconnectExtension.Companion.disconnectIfActive
import pl.spcode.navauth.velocity.infra.auth.VelocityUniqueSessionId
import pl.spcode.navauth.velocity.infra.player.VelocityPlayerAdapter
@@ -66,7 +67,7 @@ constructor(
logger.warn(
"OnServerConnect: player ${player.username} tried to connect without an auth session, disconnecting the player..."
)
- player.disconnect(
+ player.disconnectIfActive(
Component.text(
"NavAuth: user tried to connect into a server without an auth session",
TextColors.RED,
@@ -84,7 +85,7 @@ constructor(
player.uniqueId,
authSession.toString(),
)
- player.disconnect(
+ player.disconnectIfActive(
Component.text("NavAuth: bad auth state on server connect", TextColors.RED)
)
event.result = ServerPreConnectEvent.ServerResult.denied()
@@ -92,7 +93,7 @@ constructor(
}
if (!generalConfig.limboServers.contains(event.originalServer.serverInfo.name)) {
- player.disconnect(
+ player.disconnectIfActive(
Component.text(
"NavAuth: Tried to connect into different server than limbo, while waiting for limbo handler.",
TextColors.RED,
@@ -116,7 +117,7 @@ constructor(
logger.warn(
"PlayerChooseInitialServer: server tried to pick initial server for ${player.username} user without an auth session, disconnecting the player..."
)
- player.disconnect(
+ player.disconnectIfActive(
Component.text(
"NavAuth: server tried to choose an initial server for you without an auth session",
TextColors.RED,
@@ -173,7 +174,7 @@ constructor(
player.uniqueId,
authSession.toString(),
)
- player.disconnect(
+ player.disconnectIfActive(
Component.text(
"NavAuth: can't choose an initial limbo with a bad auth state",
TextColors.RED,
@@ -188,7 +189,9 @@ constructor(
"PlayerChooseInitialServerEvent: no initial limbo was found for unauthenticated user '{}'",
player.username,
)
- player.disconnect(Component.text("NavAuth: initial limbo server not found.", TextColors.RED))
+ player.disconnectIfActive(
+ Component.text("NavAuth: initial limbo server not found.", TextColors.RED)
+ )
return
}
@@ -206,7 +209,7 @@ constructor(
try {
block()
} catch (e: Throwable) {
- player.disconnect(Component.text("NavAuth: internal error", TextColors.RED))
+ player.disconnectIfActive(Component.text("NavAuth: internal error", TextColors.RED))
logger.error("unexpected internal error occurred", e)
}
}
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/listener/velocity/LoginListeners.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/listener/velocity/LoginListeners.kt
index f836a04..92093fc 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/listener/velocity/LoginListeners.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/listener/velocity/LoginListeners.kt
@@ -45,6 +45,7 @@ import pl.spcode.navauth.common.domain.user.User
import pl.spcode.navauth.common.domain.user.UserUuid
import pl.spcode.navauth.common.domain.user.Username
import pl.spcode.navauth.velocity.application.auth.session.VelocityAuthSessionFactory
+import pl.spcode.navauth.velocity.extension.PlayerDisconnectExtension.Companion.disconnectIfActive
import pl.spcode.navauth.velocity.infra.auth.VelocityUniqueSessionId
class LoginListeners
@@ -67,9 +68,8 @@ constructor(
val connUsername = event.username
if (usernameValidator.isValid(connUsername).not()) {
- // todo send error message
- event.result =
- PreLoginEvent.PreLoginComponentResult.denied(Component.text("Invalid username"))
+ val reason = messagesConfig.invalidUsernameError.toComponent()
+ event.result = PreLoginEvent.PreLoginComponentResult.denied(reason)
return
}
@@ -139,7 +139,7 @@ constructor(
player.uniqueId,
)
// there must be an auth session for specified user, otherwise abort
- player.disconnect(
+ player.disconnectIfActive(
Component.text("NavAuth: Auth session expired, please try again", TextColors.RED)
)
return
@@ -213,7 +213,7 @@ constructor(
player.uniqueId,
handshakeSession.toString(),
)
- player.disconnect(Component.text("NavAuth: Bad auth state", TextColors.RED))
+ player.disconnectIfActive(Component.text("NavAuth: Bad auth state", TextColors.RED))
}
private fun createAndStorePremiumUser(player: Player) {
diff --git a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/multification/VelocityMultification.kt b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/multification/VelocityMultification.kt
index d38e491..dab67c1 100644
--- a/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/multification/VelocityMultification.kt
+++ b/navauth-velocity/src/main/kotlin/pl/spcode/navauth/velocity/multification/VelocityMultification.kt
@@ -20,6 +20,8 @@ package pl.spcode.navauth.velocity.multification
import com.eternalcode.multification.Multification
import com.eternalcode.multification.adventure.AudienceConverter
+import com.eternalcode.multification.notice.Notice
+import com.eternalcode.multification.notice.NoticeBroadcast
import com.eternalcode.multification.translation.TranslationProvider
import com.eternalcode.multification.viewer.ViewerProvider
import com.google.inject.Inject
@@ -32,9 +34,25 @@ import pl.spcode.navauth.common.config.MessagesConfig
open class VelocityMultification
@Inject
-constructor(private val config: MessagesConfig, private val provider: VelocityViewerProvider) :
+constructor(val config: MessagesConfig, private val provider: VelocityViewerProvider) :
Multification() {
+ fun send(sender: CommandSource, notice: (config: MessagesConfig) -> Notice) {
+ create(sender, notice).send()
+ }
+
+ fun create(
+ sender: CommandSource,
+ notice: (config: MessagesConfig) -> Notice,
+ ): NoticeBroadcast<*, *, *> {
+ val notification = notice(config)
+ return if (sender !is Player) {
+ create().notice(notification).console()
+ } else {
+ create().notice(notification).player(sender.uniqueId)
+ }
+ }
+
override fun viewerProvider(): ViewerProvider {
return provider
}