Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -214,23 +214,25 @@ public void processJoin(Player player) {
} else {
ProxySessionManager.ProxyLoginRequest proxyLoginRequest = proxySessionManager.consumeLoginRequest(name);
if (proxyLoginRequest != null) {
if (!proxyLoginRequestValidator.validate(player, proxyLoginRequest.verifiedPremiumUuid())) {
if (proxyLoginRequestValidator.validate(player, proxyLoginRequest.verifiedPremiumUuid())) {
if (playerCache.isAuthenticated(name)) {
return;
}
service.send(player, MessageKey.SESSION_RECONNECTION);
// Run commands
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player,
() -> commandManager.runCommandsOnSessionLogin(player));
// Use forceLoginFromProxy (quiet=true, no BungeeCord redirect) so that if
// BungeeReceiver.performLogin() concurrently already completed the login, this
// call is a no-op rather than sending an "already logged in" error.
bukkitService.runTaskOptionallyAsync(() -> asynchronousLogin.forceLoginFromProxy(player));
logger.info("The user " + player.getName() + " has been automatically logged in, "
+ "as present in autologin queue.");
return;
}
if (playerCache.isAuthenticated(name)) {
return;
}
service.send(player, MessageKey.SESSION_RECONNECTION);
// Run commands
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player,
() -> commandManager.runCommandsOnSessionLogin(player));
// Use forceLoginFromProxy (quiet=true, no BungeeCord redirect) so that if
// BungeeReceiver.performLogin() concurrently already completed the login, this
// call is a no-op rather than sending an "already logged in" error.
bukkitService.runTaskOptionallyAsync(() -> asynchronousLogin.forceLoginFromProxy(player));
logger.info("The user " + player.getName() + " has been automatically logged in, "
+ "as present in autologin queue.");
return;
// Validation failed (e.g. premium UUID mismatch after /freemium).
// Fall through to session check / limbo flow below to avoid leaving the
// player stuck: not authenticated, no dialog, no movement allowed.
}
}
if (sessionService.canResumeSession(player)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,25 @@ public void shouldFinalizePendingPremiumInJoinFromQueuedProxyRequest() {
verify(bungeeSender, never()).sendPremiumUnset("Bobby");
}

@Test
public void shouldFallThroughToSessionOrLimboWhenProxyValidationFails() {
// given — proxy sends perform.login with a premium UUID, but the player just ran /freemium,
// so the proxy login request cannot be validated. Must fall through to session/limbo flow
// instead of leaving the player stuck with no authentication and no dialog.
Player player = mockPlayer("Bobby");
setUpRegisteredJoin(player);
given(proxySessionManager.consumeLoginRequest("bobby"))
.willReturn(new ProxySessionManager.ProxyLoginRequest("bobby", UUID.randomUUID()));
given(proxyLoginRequestValidator.validate(eq(player), any())).willReturn(false);

// when
asynchronousJoin.processJoin(player);

// then — falls through to session check; session is not valid so limbo + dialog
verify(limboService).createLimboPlayer(player, true);
verify(asynchronousLogin, never()).forceLoginFromProxy(player);
}

@Test
public void shouldForceLoginPlayerApprovedViaPreJoinDialog() {
// given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.DialogWindowService;
import fr.xephi.authme.service.PendingPremiumCache;
import fr.xephi.authme.service.PreJoinDialogService;
import fr.xephi.authme.service.PremiumLoginVerifier;
import fr.xephi.authme.service.SessionService;
Expand Down Expand Up @@ -75,6 +76,9 @@ public class PaperDialogFlowListener implements Listener {
@Inject
private PreJoinDialogService preJoinDialogService;

@Inject
private PendingPremiumCache pendingPremiumCache;

@Inject
private DialogWindowService dialogWindowService;

Expand Down Expand Up @@ -207,7 +211,11 @@ private void handleBlockingLoginDialog(PlayerConfigurationConnection connection,
// phase between the shouldSkipDialogs() check and now: if a proxy session has been queued,
// force-login instead of showing the dialog.
if (proxySessionManager.shouldResumeSession(normalizedName)) {
preJoinDialogService.approvePreJoinForceLogin(normalizedName);
ProxySessionManager.ProxyLoginRequest req = proxySessionManager.getLoginRequest(normalizedName);
if (req != null && (req.verifiedPremiumUuid() == null
|| isProxyPremiumRequestValid(normalizedName, req))) {
preJoinDialogService.approvePreJoinForceLogin(normalizedName);
}
}

if (!loginResponse.isDone()) {
Expand Down Expand Up @@ -410,14 +418,38 @@ private boolean shouldSkipPreJoinDialogForPremium(PlayerAuth auth, String player
return true;
}

private boolean isProxyPremiumRequestValid(String normalizedName, ProxySessionManager.ProxyLoginRequest request) {
UUID verifiedUuid = request.verifiedPremiumUuid();
if (verifiedUuid == null) {
return true;
}
PlayerAuth auth = dataSource.getAuth(normalizedName);
if (auth == null) {
return false;
}
if (auth.isPremium()) {
return verifiedUuid.equals(auth.getPremiumUuid());
}
UUID pendingUuid = pendingPremiumCache.getPendingUuid(normalizedName);
return pendingUuid != null && verifiedUuid.equals(pendingUuid);
}

// MC 1.21.6 (protocol 771) introduced the dialog / custom-click packets required for pre-join dialogs
private static final int DIALOG_MIN_PROTOCOL = 771;

private boolean shouldSkipDialogs(String normalizedName, PlayerConfigurationConnection connection) {
if (playerCache.isAuthenticated(normalizedName) || proxySessionManager.shouldResumeSession(normalizedName)) {
if (playerCache.isAuthenticated(normalizedName)) {
return true;
}

if (proxySessionManager.shouldResumeSession(normalizedName)) {
ProxySessionManager.ProxyLoginRequest request = proxySessionManager.getLoginRequest(normalizedName);
if (request != null && (request.verifiedPremiumUuid() == null
|| isProxyPremiumRequestValid(normalizedName, request))) {
return true;
}
}

InetSocketAddress clientAddress = connection.getClientAddress();
String ipAddress = clientAddress == null ? null : clientAddress.getAddress().getHostAddress();
if (sessionService.hasValidSession(normalizedName, ipAddress)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,8 @@ public void shouldSkipPreJoinDialogsForProxyAutoLogin() throws Exception {
given(commonService.getProperty(RestrictionSettings.UNRESTRICTED_NAMES)).willReturn(Set.of());
given(playerCache.isAuthenticated("bobby")).willReturn(false);
given(proxySessionManager.shouldResumeSession("bobby")).willReturn(true);
given(proxySessionManager.getLoginRequest("bobby"))
.willReturn(new ProxySessionManager.ProxyLoginRequest("bobby", null));

UUID playerId = UUID.randomUUID();
PlayerProfile profile = mock(PlayerProfile.class);
Expand Down
Loading