Skip to content
Open
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
21 changes: 21 additions & 0 deletions authme-core/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,27 @@ public void forceLogin(Player player) {
management.forceLogin(player);
}

/**
* Force a player to login by name, bypassing password verification.
* This is intended for trusted proxy integrations where the proxy has already
* authenticated the player.
*
* <p>The request is queued for up to 15 seconds. If the player is already online,
* the existing quiet force-login path is used. If the player is in a pre-join
* login dialog, the dialog is closed and the player is marked for force-login on join.
* If the player is offline, the queued request is consumed when the player joins.
*
* <p>Authentication completion is asynchronous. Observe {@link LoginEvent},
* {@link RestoreSessionEvent}, or {@link AuthMeApi#isAuthenticated(Player)} later;
* do not assume this method returns after authentication.
*
* @param playerName the player name (case-insensitive)
* @throws IllegalArgumentException if playerName is null
*/
public void forceLoginFromProxy(String playerName) {
management.forceLoginFromProxy(playerName);
}

/**
* Force a player to logout.
*
Expand Down
21 changes: 21 additions & 0 deletions authme-core/src/main/java/fr/xephi/authme/process/Management.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@

import javax.inject.Inject;

import fr.xephi.authme.data.ProxySessionManager;
import fr.xephi.authme.service.PreJoinDialogService;
import java.util.Locale;

/**
* Performs auth actions, e.g. when a player joins, registers or wants to change his password.
*/
Expand All @@ -27,6 +31,12 @@ public class Management {
@Inject
private BukkitService bukkitService;

@Inject
private ProxySessionManager proxySessionManager;

@Inject
private PreJoinDialogService preJoinDialogService;

// Processes
@Inject
private AsyncAddEmail asyncAddEmail;
Expand Down Expand Up @@ -65,6 +75,17 @@ public void forceLoginFromProxy(Player player) {
runTask(() -> asynchronousLogin.forceLoginFromProxy(player));
}

public void forceLoginFromProxy(String playerName) {
String normalizedName = playerName.toLowerCase(Locale.ROOT);
proxySessionManager.processProxySessionMessage(playerName);
Player player = bukkitService.getPlayerExact(playerName);
if (player != null && player.isOnline()) {
forceLoginFromProxy(player);
} else {
preJoinDialogService.approvePreJoinForceLogin(normalizedName);
}
}

public void performLogout(Player player) {
// Capture location on the main thread before handing off to the async task.
Location quitLocation = player.getLocation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,18 @@ void shouldForceLogin() {
verify(management).forceLogin(player);
}

@Test
void shouldForceLoginFromProxyByName() {
// given
String playerName = "Connor";

// when
api.forceLoginFromProxy(playerName);

// then
verify(management).forceLoginFromProxy(playerName);
}

@Test
void shouldForceLogout() {
// given
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package fr.xephi.authme.process;

import fr.xephi.authme.data.ProxySessionManager;
import fr.xephi.authme.process.login.AsynchronousLogin;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.PreJoinDialogService;
import org.bukkit.entity.Player;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
* Test for {@link Management}.
*/
@ExtendWith(MockitoExtension.class)
class ManagementTest {

@InjectMocks
private Management management;

@Mock
private BukkitService bukkitService;
@Mock
private AsynchronousLogin asynchronousLogin;
@Mock
private ProxySessionManager proxySessionManager;
@Mock
private PreJoinDialogService preJoinDialogService;

@Test
void shouldForceLoginFromProxyByNameQueuesBeforeOnlineForceLogin() {
// given
String playerName = "Connor";
Player player = mock(Player.class);
when(bukkitService.getPlayerExact(playerName)).thenReturn(player);
when(player.isOnline()).thenReturn(true);

// when
management.forceLoginFromProxy(playerName);

// then
verify(proxySessionManager).processProxySessionMessage(playerName);
verify(bukkitService).runTaskOptionallyAsync(any(Runnable.class));
}

@Test
void shouldForceLoginFromProxyByNameApprovesBlockingPreJoinDialog() {
// given
String playerName = "ConNor";
when(bukkitService.getPlayerExact(playerName)).thenReturn(null);

// when
management.forceLoginFromProxy(playerName);

// then
verify(proxySessionManager).processProxySessionMessage(playerName);
verify(preJoinDialogService).approvePreJoinForceLogin("connor");
verify(bukkitService, never()).runTaskOptionallyAsync(any());
}

@Test
void shouldForceLoginFromProxyByNameKeepsQueueWhenNoDialogFutureExists() {
// given
String playerName = "OfflinePlayer";
when(bukkitService.getPlayerExact(playerName)).thenReturn(null);
when(preJoinDialogService.approvePreJoinForceLogin("offlineplayer")).thenReturn(false);

// when
management.forceLoginFromProxy(playerName);

// then
verify(proxySessionManager).processProxySessionMessage(playerName);
verify(preJoinDialogService).approvePreJoinForceLogin("offlineplayer");
verify(bukkitService, never()).runTaskOptionallyAsync(any());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ void shouldOnlyQueueSessionWhenPerformLoginReceivedForOfflinePlayer() {

// then
verify(proxySessionManager).processProxySessionMessage(playerName, null);
verify(management, never()).forceLoginFromProxy(any());
verify(management, never()).forceLoginFromProxy(any(Player.class));
verify(bungeeSender, never()).sendAuthMeBungeecordMessage(any(), any());
}

Expand Down Expand Up @@ -234,7 +234,7 @@ void shouldRemoveQueuedRequestWhenPremiumValidateRejects() {

// then
verify(proxySessionManager).removeLoginRequest(playerName);
verify(management, never()).forceLoginFromProxy(any());
verify(management, never()).forceLoginFromProxy(any(Player.class));
verify(bungeeSender, never()).sendAuthMeBungeecordMessage(any(), any());
}

Expand All @@ -261,7 +261,7 @@ void shouldValidateAndQueueConfigPhasePerformLogin() {
// then
assertThat(result, equalTo("bobby"));
verify(proxySessionManager).processProxySessionMessage(playerName, null);
verify(management, never()).forceLoginFromProxy(any());
verify(management, never()).forceLoginFromProxy(any(Player.class));
}

@Test
Expand Down