Skip to content

Commit

Permalink
Add httpBasic for FakePlayer
Browse files Browse the repository at this point in the history
blacelle committed Sep 24, 2024
1 parent e889763 commit 2ac66ce
Showing 48 changed files with 630 additions and 219 deletions.
8 changes: 7 additions & 1 deletion authorization/pom.xml
Original file line number Diff line number Diff line change
@@ -28,7 +28,13 @@
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.41.1</version>
</dependency>


<dependency>
<!-- JwtAuthenticationToken, for the sole purpose of inferring the accountId authoring a contest-->
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package eu.solven.kumite.account;

import java.util.UUID;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;

import eu.solven.kumite.security.LoginRouteButNotAuthenticatedException;
import reactor.core.publisher.Mono;

public class JwtUserContextHolder implements IKumiteUserContextHolder {

@Override
public Mono<UUID> authenticatedAccountId() {
return ReactiveSecurityContextHolder.getContext().map(securityContext -> {
Authentication authentication = securityContext.getAuthentication();

if (authentication instanceof JwtAuthenticationToken jwtAuth) {
UUID accountId = UUID.fromString(jwtAuth.getToken().getSubject());

return accountId;
} else {
throw new LoginRouteButNotAuthenticatedException("Expecting a JWT token");
}
});
}

}
18 changes: 0 additions & 18 deletions contest-core/pom.xml
Original file line number Diff line number Diff line change
@@ -27,24 +27,6 @@
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

<dependency>
<!-- ReactiveSecurityContextHolder, for the sole purpose of inferring the accountId authoring a contest -->
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>

<dependency>
<!-- JwtAuthenticationToken, for the sole purpose of inferring the accountId authoring a contest-->
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>

<dependency>
<!-- Jwt, for the sole purpose of inferring the accountId authoring a contest-->
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Original file line number Diff line number Diff line change
@@ -5,35 +5,24 @@
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.beans.factory.InitializingBean;

import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.account.fake_player.FakePlayer;
import eu.solven.kumite.account.fake_player.RandomPlayer;
import eu.solven.kumite.player.IAccountPlayersRegistry;
import eu.solven.kumite.player.KumitePlayer;
import eu.solven.kumite.tools.IUuidGenerator;
import eu.solven.kumite.tools.JdkUuidGenerator;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@AllArgsConstructor
@Slf4j
public class InMemoryUserRepository implements IKumiteUserRepository, IKumiteUserRawRawRepository, InitializingBean {
public class InMemoryUserRepository implements IKumiteUserRepository, IKumiteUserRawRawRepository {
final Map<KumiteUserRawRaw, KumiteUser> accountIdToUser = new ConcurrentHashMap<>();
final Map<UUID, KumiteUserRawRaw> accountIdToRawRaw = new ConcurrentHashMap<>();

final IUuidGenerator uuidGenerator;

final IAccountPlayersRegistry playersRegistry;

@Override
public void afterPropertiesSet() {
KumiteUser fakeUser = FakePlayerTokens.fakeUser();
KumiteUserRawRaw rawRaw = fakeUser.getRaw().getRawRaw();

accountIdToRawRaw.put(fakeUser.getAccountId(), rawRaw);
accountIdToUser.put(rawRaw, fakeUser);
}

@Override
public Optional<KumiteUser> getUser(KumiteUserRawRaw accountId) {
return Optional.ofNullable(accountIdToUser.get(accountId));
@@ -56,7 +45,7 @@ public KumiteUser registerOrUpdate(KumiteUserRaw kumiteUserRaw) {
return accountIdToUser.compute(rawRaw, (k, alreadyIn) -> {
KumiteUser.KumiteUserBuilder kumiteUserBuilder = KumiteUser.builder().raw(kumiteUserRaw);
if (alreadyIn == null) {
UUID accountId = uuidGenerator.randomUUID();
UUID accountId = generateAccountId(rawRaw);

KumitePlayer player = register(rawRaw, accountId);

@@ -72,6 +61,19 @@ public KumiteUser registerOrUpdate(KumiteUserRaw kumiteUserRaw) {
});
}

protected UUID generateAccountId(KumiteUserRawRaw rawRaw) {
return generateAccountId(uuidGenerator, rawRaw);
}

public static UUID generateAccountId(IUuidGenerator uuidGenerator, KumiteUserRawRaw rawRaw) {
if (rawRaw.equals(FakePlayer.user().getRaw().getRawRaw())) {
return FakePlayer.ACCOUNT_ID;
} else if (rawRaw.equals(RandomPlayer.user().getRaw().getRawRaw())) {
return RandomPlayer.ACCOUNT_ID;
}
return uuidGenerator.randomUUID();
}

private KumitePlayer register(KumiteUserRawRaw rawRaw, UUID accountId) {
UUID playerId = playersRegistry.generateMainPlayerId(accountId);

@@ -83,12 +85,4 @@ private KumitePlayer register(KumiteUserRawRaw rawRaw, UUID accountId) {

return player;
}

public static InMemoryUserRepository forTests(IAccountPlayersRegistry playersRegistry) {
InMemoryUserRepository inMemoryUserRepository =
new InMemoryUserRepository(JdkUuidGenerator.INSTANCE, playersRegistry);
inMemoryUserRepository.afterPropertiesSet();
return inMemoryUserRepository;
}

}
Original file line number Diff line number Diff line change
@@ -31,4 +31,10 @@ public IHasBoard makeDynamicBoardHolder(UUID contestId) {
public void updateBoard(UUID contestId, IKumiteBoard currentBoard) {
boardRepository.updateBoard(contestId, currentBoard);
}

public void forceGameover(UUID contestId) {
// currentBoard.
// TODO Auto-generated method stub

}
}
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
import java.util.UUID;
import java.util.random.RandomGenerator;

import eu.solven.kumite.account.KumiteUser;
import eu.solven.kumite.account.fake_player.RandomPlayer;
import eu.solven.kumite.app.IKumiteSpringProfiles;
import eu.solven.kumite.board.IKumiteBoard;
import eu.solven.kumite.game.GameSearchParameters;
@@ -51,7 +51,7 @@ public void makeContestsIfNoneJoinable() {
String contestName = "Auto-generated " + randomGenerator.nextInt(128);
ContestCreationMetadata constantMetadata = ContestCreationMetadata.fromGame(gameMetadata)
.name(contestName)
.author(KumiteUser.SERVER_ACCOUNTID)
.author(RandomPlayer.ACCOUNT_ID)
.build();
Contest contest = contestsRegistry.registerContest(game, constantMetadata, initialBoard);

Original file line number Diff line number Diff line change
@@ -7,23 +7,20 @@
import java.util.stream.Collectors;

import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;

import com.fasterxml.jackson.databind.ObjectMapper;

import eu.solven.kumite.account.IKumiteUserContextHolder;
import eu.solven.kumite.app.KumiteJackson;
import eu.solven.kumite.app.webflux.api.KumiteHandlerHelper;
import eu.solven.kumite.board.IKumiteBoard;
import eu.solven.kumite.contest.ContestSearchParameters.ContestSearchParametersBuilder;
import eu.solven.kumite.game.GameMetadata;
import eu.solven.kumite.game.GamesRegistry;
import eu.solven.kumite.game.IGame;
import eu.solven.kumite.security.LoginRouteButNotAuthenticatedException;
import io.micrometer.common.util.StringUtils;
import lombok.Builder;
import lombok.NonNull;
@@ -47,6 +44,9 @@ public class ContestHandler {
@NonNull
final RandomGenerator randomGenerator;

@NonNull
final IKumiteUserContextHolder kumiteUser;

public Mono<ServerResponse> listContests(ServerRequest request) {
ContestSearchParametersBuilder parameters = ContestSearchParameters.builder();

@@ -74,23 +74,11 @@ public Mono<ServerResponse> listContests(ServerRequest request) {
}

// BEWARE we coupled the generation of a contest and its board. This may be poor design.
public Mono<ServerResponse> generateContest(ServerRequest request) {
public Mono<ServerResponse> openContest(ServerRequest request) {
UUID gameId = KumiteHandlerHelper.uuid(request, "game_id");
IGame game = gamesRegistry.getGame(gameId);

return ReactiveSecurityContextHolder.getContext().map(securityContext -> {
Authentication authentication = securityContext.getAuthentication();

if (authentication instanceof JwtAuthenticationToken jwtAuth) {
// jwtAuth.ge

UUID accountId = UUID.fromString(jwtAuth.getToken().getSubject());

return accountId;
} else {
throw new LoginRouteButNotAuthenticatedException("Expecting a JWT token");
}
}).flatMap(authorAccountId -> {
return kumiteUser.authenticatedAccountId().flatMap(authorAccountId -> {
return request.bodyToMono(Map.class).<ServerResponse>flatMap(contestBody -> {
Map<String, ?> rawConstantMetadata = (Map<String, ?>) contestBody.get("constant_metadata");

@@ -151,4 +139,25 @@ private ContestCreationMetadata validateConstantMetadata(UUID authorAccountId,
return mergedContestMetadata;
}

public Mono<ServerResponse> deleteContest(ServerRequest request) {
UUID contestId = KumiteHandlerHelper.uuid(request, "contest_id");

return kumiteUser.authenticatedAccountId().flatMap(authorAccountId -> {
ContestCreationMetadata contest = contestsRegistry.contestsRepository.getById(contestId)
.orElseThrow(() -> new IllegalArgumentException("There is no contest for contestId=" + contestId));

if (!contest.getAuthor().equals(authorAccountId)) {
throw new IllegalArgumentException("Can not DELETE contestId=%s as its author is not you (but %s)"
.formatted(contestId, contest.getAuthor()));
}

contestsRegistry.deleteContest(contestId);

return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(Map.of("contestId", contestId, "author", authorAccountId)));
});

}

}
Original file line number Diff line number Diff line change
@@ -127,4 +127,10 @@ public List<Contest> searchContests(ContestSearchParameters search) {

return metaStream.collect(Collectors.toList());
}

public void deleteContest(UUID contestId) {
boardsRegistry.forceGameover(contestId);
contestPlayersRegistry.forceGameover(contestId);
contestsRepository.archive(contestId);
}
}
Original file line number Diff line number Diff line change
@@ -27,4 +27,6 @@ public interface IContestsRepository {

Stream<Map.Entry<UUID, ContestCreationMetadata>> stream();

void archive(UUID contestId);

}
Original file line number Diff line number Diff line change
@@ -36,4 +36,9 @@ public void clear() {
log.info("We reset {} contests", size);
uuidToContests.clear();
}

@Override
public void archive(UUID contestId) {
// TODO Should we remove the contest?
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package eu.solven.kumite.player;

import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

@@ -51,4 +52,13 @@ public IHasPlayers makeDynamicHasPlayers(UUID contestId) {
}).collect(Collectors.toList());
}

@Override
public void gameover(UUID contestId) {
Optional<IKumiteBoard> board = boardRepository.getBoard(contestId);
if (board.isPresent()) {
// TODO Checkif the board is actually over
// board.get().
}
}

}
Original file line number Diff line number Diff line change
@@ -166,4 +166,9 @@ public PlayerContestStatus getPlayingPlayer(UUID playerId, Contest contestMetada
return playingPlayer;

}

public void forceGameover(UUID contestId) {
contestToViewingAccounts.remove(contestId);
contestPlayersRepository.gameover(contestId);
}
}
Original file line number Diff line number Diff line change
@@ -24,4 +24,6 @@ public interface IContendersRepository {

IHasPlayers makeDynamicHasPlayers(UUID contestId);

void gameover(UUID contestId);

}
Original file line number Diff line number Diff line change
@@ -43,4 +43,9 @@ public boolean isContender(UUID contestId, UUID playerId) {
return viewContenders(contestId).stream().anyMatch(somePlayerId -> somePlayerId.equals(playerId));
}

@Override
public void gameover(UUID contestId) {
contestToContenders.remove(contestId);
}

}
Original file line number Diff line number Diff line change
@@ -5,7 +5,8 @@
import java.util.List;
import java.util.UUID;

import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.account.fake_player.FakePlayer;
import eu.solven.kumite.account.fake_player.RandomPlayer;
import eu.solven.kumite.player.IAccountPlayersRegistry;
import eu.solven.kumite.player.IHasPlayers;
import eu.solven.kumite.player.KumitePlayer;
@@ -25,8 +26,10 @@ public final class BijectiveAccountPlayersRegistry implements IAccountPlayersReg
public void registerPlayer(KumitePlayer player) {
UUID accountId = player.getAccountId();
UUID playerId = player.getPlayerId();
if (FakePlayerTokens.FAKE_ACCOUNT_ID.equals(accountId) && FakePlayerTokens.isFakePlayer(playerId)) {
if (FakePlayer.ACCOUNT_ID.equals(accountId) && FakePlayer.isFakePlayer(playerId)) {
log.info("Registering the fakeUser");
} else if (RandomPlayer.ACCOUNT_ID.equals(accountId) && RandomPlayer.isRandomPlayer(playerId)) {
log.info("Registering the randomuser");
} else if (playerId.equals(generateMainPlayerId(accountId))) {
log.info("Registering accountId={} playerId={}", accountId, playerId);
} else {
@@ -36,17 +39,22 @@ public void registerPlayer(KumitePlayer player) {

@Override
public UUID getAccountId(UUID playerId) {
if (FakePlayerTokens.isFakePlayer(playerId)) {
return FakePlayerTokens.FAKE_ACCOUNT_ID;
if (FakePlayer.isFakePlayer(playerId)) {
return FakePlayer.ACCOUNT_ID;
} else if (RandomPlayer.isRandomPlayer(playerId)) {
return RandomPlayer.ACCOUNT_ID;
}

return accountIdGivenPlayerId(playerId);
}

@Override
public IHasPlayers makeDynamicHasPlayers(UUID accountId) {
if (FakePlayerTokens.FAKE_ACCOUNT_ID.equals(accountId)) {
List<KumitePlayer> players = Arrays.asList(FakePlayerTokens.fakePlayer(0), FakePlayerTokens.fakePlayer(1));
if (FakePlayer.ACCOUNT_ID.equals(accountId)) {
List<KumitePlayer> players = Arrays.asList(FakePlayer.fakePlayer(0), FakePlayer.fakePlayer(1));
return () -> players;
} else if (RandomPlayer.ACCOUNT_ID.equals(accountId)) {
List<KumitePlayer> players = Arrays.asList(RandomPlayer.randomPlayer(0), RandomPlayer.randomPlayer(1));
return () -> players;
}

@@ -58,8 +66,10 @@ public IHasPlayers makeDynamicHasPlayers(UUID accountId) {

@Override
public UUID generateMainPlayerId(UUID accountId) {
if (FakePlayerTokens.FAKE_ACCOUNT_ID.equals(accountId)) {
return FakePlayerTokens.FAKE_PLAYER_ID1;
if (FakePlayer.ACCOUNT_ID.equals(accountId)) {
return FakePlayer.PLAYER_ID1;
} else if (RandomPlayer.ACCOUNT_ID.equals(accountId)) {
return RandomPlayer.PLAYERID_1;
}

return playerIdGivenAccountId(accountId);
Original file line number Diff line number Diff line change
@@ -7,7 +7,8 @@
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.stream.Collectors;

import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.account.fake_player.FakePlayer;
import eu.solven.kumite.account.fake_player.RandomPlayer;
import eu.solven.kumite.player.IAccountPlayersRegistry;
import eu.solven.kumite.player.IHasPlayers;
import eu.solven.kumite.player.KumitePlayer;
@@ -27,9 +28,6 @@ public final class InMemoryAccountPlayersRegistry implements IAccountPlayersRegi

public InMemoryAccountPlayersRegistry(IUuidGenerator uuidGenerator) {
this.uuidGenerator = uuidGenerator;

registerPlayer(FakePlayerTokens.fakePlayer());
registerPlayer(FakePlayerTokens.fakePlayer(1));
}

@Override
@@ -50,10 +48,6 @@ public void registerPlayer(KumitePlayer player) {

@Override
public UUID getAccountId(UUID playerId) {
if (FakePlayerTokens.isFakePlayer(playerId)) {
return FakePlayerTokens.FAKE_ACCOUNT_ID;
}

UUID accountId = playerIdToAccountId.get(playerId);
if (accountId == null) {
throw new IllegalArgumentException("Unknown playerId: " + playerId);
@@ -72,6 +66,12 @@ public IHasPlayers makeDynamicHasPlayers(UUID accountId) {

@Override
public UUID generateMainPlayerId(UUID accountId) {
if (FakePlayer.ACCOUNT_ID.equals(accountId)) {
return FakePlayer.PLAYER_ID1;
} else if (RandomPlayer.ACCOUNT_ID.equals(accountId)) {
return RandomPlayer.PLAYERID_1;
}

return uuidGenerator.randomUUID();
}
}
Original file line number Diff line number Diff line change
@@ -6,18 +6,18 @@
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.account.fake_player.FakePlayer;
import eu.solven.kumite.player.persistence.BijectiveAccountPlayersRegistry;

public class TestBijectiveAccountPlayersRegistry {
IAccountPlayersRegistry playersRegistry = new BijectiveAccountPlayersRegistry();

@Test
public void testFakePlayer() {
Assertions.assertThat(playersRegistry.getAccountId(FakePlayerTokens.FAKE_PLAYER_ID1))
.isEqualTo(FakePlayerTokens.FAKE_ACCOUNT_ID);
Assertions.assertThat(playersRegistry.getAccountId(FakePlayerTokens.FAKE_PLAYER_ID2))
.isEqualTo(FakePlayerTokens.FAKE_ACCOUNT_ID);
Assertions.assertThat(playersRegistry.getAccountId(FakePlayer.PLAYER_ID1))
.isEqualTo(FakePlayer.ACCOUNT_ID);
Assertions.assertThat(playersRegistry.getAccountId(FakePlayer.PLAYER_ID2))
.isEqualTo(FakePlayer.ACCOUNT_ID);
}

@Test
@@ -38,16 +38,16 @@ public void testNormalPlayer() {
@Test
public void testHasPlayers_FakePlayers() {
List<KumitePlayer> players =
playersRegistry.makeDynamicHasPlayers(FakePlayerTokens.FAKE_ACCOUNT_ID).getPlayers();
playersRegistry.makeDynamicHasPlayers(FakePlayer.ACCOUNT_ID).getPlayers();

Assertions.assertThat(players)
.contains(KumitePlayer.builder()
.playerId(FakePlayerTokens.FAKE_PLAYER_ID1)
.accountId(FakePlayerTokens.FAKE_ACCOUNT_ID)
.playerId(FakePlayer.PLAYER_ID1)
.accountId(FakePlayer.ACCOUNT_ID)
.build())
.contains(KumitePlayer.builder()
.playerId(FakePlayerTokens.FAKE_PLAYER_ID2)
.accountId(FakePlayerTokens.FAKE_ACCOUNT_ID)
.playerId(FakePlayer.PLAYER_ID2)
.accountId(FakePlayer.ACCOUNT_ID)
.build())
.hasSize(2);
}
Original file line number Diff line number Diff line change
@@ -5,34 +5,38 @@
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.account.fake_player.FakePlayer;
import eu.solven.kumite.player.persistence.InMemoryAccountPlayersRegistry;
import eu.solven.kumite.tools.JdkUuidGenerator;

public class TestInMemoryAccountPlayersRegistry {
IAccountPlayersRegistry playersRegistry = new InMemoryAccountPlayersRegistry(new JdkUuidGenerator());

private void registerFakePlayers() {
playersRegistry.registerPlayer(FakePlayer.fakePlayer());
playersRegistry.registerPlayer(FakePlayer.fakePlayer(1));
}

@Test
public void testFakePlayer() {
Assertions.assertThat(playersRegistry.getAccountId(FakePlayerTokens.FAKE_PLAYER_ID1))
.isEqualTo(FakePlayerTokens.FAKE_ACCOUNT_ID);
Assertions.assertThat(playersRegistry.getAccountId(FakePlayerTokens.FAKE_PLAYER_ID2))
.isEqualTo(FakePlayerTokens.FAKE_ACCOUNT_ID);
registerFakePlayers();

Assertions.assertThat(playersRegistry.getAccountId(FakePlayer.PLAYER_ID1)).isEqualTo(FakePlayer.ACCOUNT_ID);
Assertions.assertThat(playersRegistry.getAccountId(FakePlayer.PLAYER_ID2)).isEqualTo(FakePlayer.ACCOUNT_ID);
}

@Test
public void testHasPlayers_FakePlayers() {
List<KumitePlayer> players =
playersRegistry.makeDynamicHasPlayers(FakePlayerTokens.FAKE_ACCOUNT_ID).getPlayers();
registerFakePlayers();

List<KumitePlayer> players = playersRegistry.makeDynamicHasPlayers(FakePlayer.ACCOUNT_ID).getPlayers();

Assertions.assertThat(players)
.contains(
KumitePlayer.builder().playerId(FakePlayer.PLAYER_ID1).accountId(FakePlayer.ACCOUNT_ID).build())
.contains(KumitePlayer.builder()
.playerId(FakePlayerTokens.FAKE_PLAYER_ID1)
.accountId(FakePlayerTokens.FAKE_ACCOUNT_ID)
.build())
.contains(KumitePlayer.builder()
.playerId(FakePlayerTokens.FAKE_PLAYER_ID2)
.accountId(FakePlayerTokens.FAKE_ACCOUNT_ID)
.playerId(FakePlayer.PLAYER_ID2)
.accountId(FakePlayer.ACCOUNT_ID)
.build())
.hasSize(2);
}
38 changes: 38 additions & 0 deletions js/src/main/resources/static/ui/js/kumite-contest-delete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ref, onMounted, onUnmounted } from "vue";

import { mapState } from "pinia";
import { useKumiteStore } from "./store.js";

export default {
props: {
contestId: {
type: String,
required: true,
},
gameId: {
type: String,
required: true,
},
},
computed: {
...mapState(useKumiteStore, ["nbGameFetching", "nbContestFetching"]),
...mapState(useKumiteStore, {
game(store) {
return store.games[this.gameId];
},
contest(store) {
return store.contests[this.contestId];
},
}),
},
setup(props) {
const store = useKumiteStore();

store.loadContestIfMissing(props.gameId, props.contestId);

return {};
},
template: /* HTML */ `
<button class="btn btn-danger">Archive this contest (force gameOver)</button>
`,
};
5 changes: 5 additions & 0 deletions js/src/main/resources/static/ui/js/kumite-contest-header.js
Original file line number Diff line number Diff line change
@@ -4,10 +4,12 @@ import { mapState } from "pinia";
import { useKumiteStore } from "./store.js";

import KumiteAccountRef from "./kumite-account-ref.js";
import KumiteContestDelete from "./kumite-contest-delete.js";

export default {
components: {
KumiteAccountRef,
KumiteContestDelete,
},
props: {
contestId: {
@@ -93,6 +95,9 @@ export default {
<ul>
<li>author: <KumiteAccountRef :accountId="contest.constantMetadata.author" /></li>
<li>created: {{contest.constantMetadata.created}}</li>
<li v-if="contest.constantMetadata.author == account.accountId">
<KumiteContestDelete :contestId="contestId" />
</li>
</ul>
</span>
`,
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.account.fake_player.RandomPlayer;
import eu.solven.kumite.app.IKumiteSpringProfiles;
import eu.solven.kumite.app.KumiteContestServerApplication;
import eu.solven.kumite.app.KumiteWebclientServerProperties;
@@ -42,7 +42,7 @@
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = KumiteContestServerApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles({ IKumiteSpringProfiles.P_UNSAFE, IKumiteSpringProfiles.P_INMEMORY, IKumiteSpringProfiles.P_FAKEUSER })
@ActiveProfiles({ IKumiteSpringProfiles.P_UNSAFE, IKumiteSpringProfiles.P_INMEMORY })
@TestPropertySource(properties = { "kumite.random.seed=123",
"kumite.player.wait_duration_if_no_move" + "=PT0.001S",

@@ -59,7 +59,7 @@ public class TestRandomGamingLogic {

@Test
public void testOptimization() {
UUID playerId = FakePlayerTokens.FAKE_PLAYER_ID1;
UUID playerId = RandomPlayer.PLAYERID_1;

KumiteWebclientServerProperties properties = KumiteWebclientServerProperties.forTests(env, randomServerPort);
KumiteWebclientServer kumiteServer = KumiteWebclientServer.fromProperties(properties);
@@ -93,7 +93,7 @@ public void test1v1TurnBased() throws InterruptedException {
IGamingLogic kumitePlayer = new RandomGamingLogic(env, kumiteServer);

for (int iPlayer = 0; iPlayer < nbPlayers; iPlayer++) {
UUID playerId = FakePlayerTokens.fakePlayerId(iPlayer);
UUID playerId = RandomPlayer.randomPlayerId(iPlayer);

executorService.execute(() -> {
try {
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.account.fake_player.RandomPlayer;
import eu.solven.kumite.app.IKumiteSpringProfiles;
import eu.solven.kumite.app.KumiteContestServerApplication;
import eu.solven.kumite.app.KumiteWebclientServerProperties;
@@ -44,7 +44,7 @@
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = KumiteContestServerApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles({ IKumiteSpringProfiles.P_UNSAFE, IKumiteSpringProfiles.P_REDIS, IKumiteSpringProfiles.P_FAKEUSER })
@ActiveProfiles({ IKumiteSpringProfiles.P_UNSAFE, IKumiteSpringProfiles.P_REDIS })
@TestPropertySource(properties = { "kumite.random.seed=123",
"kumite.player.wait_duration_if_no_move" + "=PT0.001S",
KumiteWebclientServerProperties.KEY_PLAYER_CONTESTBASEURL + "=http://localhost:LocalServerPort",
@@ -65,7 +65,7 @@ public class TestRandomGamingLogicRedis {

@Test
public void testOptimization() {
UUID playerId = FakePlayerTokens.FAKE_PLAYER_ID1;
UUID playerId = RandomPlayer.PLAYERID_1;

KumiteWebclientServerProperties properties = KumiteWebclientServerProperties.forTests(env, randomServerPort);
IKumiteServer kumiteServer = KumiteWebclientServer.fromProperties(properties);
@@ -96,7 +96,7 @@ public void test1v1TurnBased() throws InterruptedException {
IGamingLogic kumitePlayer = new RandomGamingLogic(env, kumiteServer);

for (int iPlayer = 0; iPlayer < nbPlayers; iPlayer++) {
UUID playerId = FakePlayerTokens.fakePlayerId(iPlayer);
UUID playerId = RandomPlayer.randomPlayerId(iPlayer);

executorService.execute(() -> {
try {
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.account.fake_player.RandomPlayer;
import eu.solven.kumite.app.IKumiteSpringProfiles;
import eu.solven.kumite.app.KumiteContestServerApplication;
import eu.solven.kumite.app.KumiteWebclientServerProperties;
@@ -37,7 +37,7 @@
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = KumiteContestServerApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles({ IKumiteSpringProfiles.P_UNSAFE, IKumiteSpringProfiles.P_INMEMORY, IKumiteSpringProfiles.P_FAKEUSER })
@ActiveProfiles({ IKumiteSpringProfiles.P_UNSAFE, IKumiteSpringProfiles.P_INMEMORY })
@TestPropertySource(properties = { "kumite.random.seed=123",
"kumite.player.wait_duration_if_no_move" + "=PT0.001S",
KumiteWebclientServerProperties.KEY_PLAYER_CONTESTBASEURL + "=http://localhost:LocalServerPort" })
@@ -53,7 +53,7 @@ public class TestTSPLifecycleThroughRouter {

@Test
public void testSinglePlayer() {
UUID playerId = FakePlayerTokens.FAKE_PLAYER_ID1;
UUID playerId = RandomPlayer.PLAYERID_1;

KumiteWebclientServerProperties properties = KumiteWebclientServerProperties.forTests(env, randomServerPort);
IKumiteServer kumiteServer = KumiteWebclientServer.fromProperties(properties);
Original file line number Diff line number Diff line change
@@ -3,7 +3,8 @@
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;

import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.account.fake_player.FakePlayer;
import eu.solven.kumite.account.fake_player.RandomPlayer;
import eu.solven.kumite.login.RefreshTokenWrapper;
import eu.solven.kumite.oauth2.authorizationserver.KumiteTokenService;
import eu.solven.kumite.tools.IUuidGenerator;
@@ -20,6 +21,7 @@ public class KumiteWebclientServerProperties {

public static final String ENV_REFRESH_TOKEN = "kumite.player.refresh_token";
public static final String PLACEHOLDER_GENERATEFAKEPLAYER = "GENERATE_FAKEUSER";
public static final String PLACEHOLDER_GENERATERANDOMPLAYER = "GENERATE_RANDOMUSER";

String baseUrl;
String refreshToken;
@@ -36,8 +38,16 @@ public static String loadRefreshToken(Environment env, IUuidGenerator uuidGenera
log.info("Generating on-the-fly a fakeUser refreshToken");
}
KumiteTokenService kumiteTokenService = new KumiteTokenService(env, uuidGenerator);
RefreshTokenWrapper wrappedRefreshToken = kumiteTokenService
.wrapInJwtRefreshToken(FakePlayerTokens.fakeUser(), FakePlayerTokens.fakePlayers());
RefreshTokenWrapper wrappedRefreshToken =
kumiteTokenService.wrapInJwtRefreshToken(FakePlayer.user(), FakePlayer.fakePlayers());
refreshToken = wrappedRefreshToken.getRefreshToken();
} else if (KumiteWebclientServerProperties.PLACEHOLDER_GENERATERANDOMPLAYER.equals(refreshToken)) {
{
log.info("Generating on-the-fly a fakeUser refreshToken");
}
KumiteTokenService kumiteTokenService = new KumiteTokenService(env, uuidGenerator);
RefreshTokenWrapper wrappedRefreshToken =
kumiteTokenService.wrapInJwtRefreshToken(RandomPlayer.user(), RandomPlayer.randomPlayers());
refreshToken = wrappedRefreshToken.getRefreshToken();
}
return refreshToken;
@@ -46,7 +56,7 @@ public static String loadRefreshToken(Environment env, IUuidGenerator uuidGenera
public static KumiteWebclientServerProperties forTests(Environment env, int randomServerPort) {
String refreshToken = loadRefreshToken(env,
JdkUuidGenerator.INSTANCE,
KumiteWebclientServerProperties.PLACEHOLDER_GENERATEFAKEPLAYER);
KumiteWebclientServerProperties.PLACEHOLDER_GENERATERANDOMPLAYER);

// https://github.com/spring-projects/spring-boot/issues/5077
String baseUrl = env.getRequiredProperty(KEY_PLAYER_CONTESTBASEURL)
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import eu.solven.kumite.account.fake_player.RandomPlayer;
import lombok.extern.slf4j.Slf4j;

@ExtendWith(SpringExtension.class)
@@ -21,11 +22,11 @@
// Enables generation on-the-fly on refreshToken
IKumiteSpringProfiles.P_UNSAFE_OAUTH2,
// Enables playing as fakeUser
IKumiteSpringProfiles.P_FAKEUSER,
// IKumiteSpringProfiles.P_FAKEUSER,
IKumiteSpringProfiles.P_UNSAFE_PLAYER })
@TestPropertySource(properties = { KumiteWebclientServerProperties.KEY_PLAYER_CONTESTBASEURL + "=someUrl",
KumiteWebclientServerProperties.ENV_REFRESH_TOKEN + "="
+ KumiteWebclientServerProperties.PLACEHOLDER_GENERATEFAKEPLAYER })
+ KumiteWebclientServerProperties.PLACEHOLDER_GENERATERANDOMPLAYER })
@Slf4j
public class TestParseFakePlayer implements IKumiteSpringProfiles {

@@ -43,8 +44,8 @@ public void testPlayerIdFromAccessToken() {
Set<UUID> playerIds = conf.playerIdFromRefreshToken(kumiteWebclientServerProperties);

Assertions.assertThat(playerIds)
.contains(UUID.fromString("11111111-1111-1111-1111-111111111111"))
.contains(UUID.fromString("11111111-1111-1111-1111-222222222222"))
.contains(RandomPlayer.randomPlayer(0).getPlayerId())
.contains(RandomPlayer.randomPlayer(1).getPlayerId())
.hasSize(2);
}
}
3 changes: 0 additions & 3 deletions public/src/main/java/eu/solven/kumite/account/KumiteUser.java
Original file line number Diff line number Diff line change
@@ -21,9 +21,6 @@
@Jacksonized
@Slf4j
public class KumiteUser {
// Used to create contests
public static final UUID SERVER_ACCOUNTID = UUID.fromString("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF");

// Multiple Users may be attached to the same account (e.g. by using different OAuth2 providers)
@NonNull
UUID accountId;
Original file line number Diff line number Diff line change
@@ -17,52 +17,52 @@
*
*/
@Slf4j
public class FakePlayerTokens {
public class FakePlayer {

// IKumiteSpringProfiles.P_DEFAULT_FAKE_USER
public static final UUID FAKE_ACCOUNT_ID = UUID.fromString("11111111-1111-1111-1111-000000000000");
public static final UUID FAKE_PLAYER_ID1 = UUID.fromString("11111111-1111-1111-1111-111111111111");
public static final UUID FAKE_PLAYER_ID2 = UUID.fromString("11111111-1111-1111-1111-222222222222");
public static final UUID ACCOUNT_ID = UUID.fromString("11111111-1111-1111-1111-000000000000");
public static final UUID PLAYER_ID1 = UUID.fromString("11111111-1111-1111-1111-111111111111");
public static final UUID PLAYER_ID2 = UUID.fromString("11111111-1111-1111-1111-222222222222");

public static UUID fakePlayerId(int playerIndex) {
if (playerIndex == 0) {
return FAKE_PLAYER_ID1;
return PLAYER_ID1;
} else if (playerIndex == 1) {
return FAKE_PLAYER_ID2;
return PLAYER_ID2;
} else {
throw new IllegalArgumentException("There is no fakePlayer for playerIndex=" + playerIndex);
}
}

public static boolean isFakePlayer(UUID playerId) {
if (FAKE_PLAYER_ID1.equals(playerId) || FAKE_PLAYER_ID2.equals(playerId)) {
if (PLAYER_ID1.equals(playerId) || PLAYER_ID2.equals(playerId)) {
return true;
} else {
return false;
}
}

public static KumiteUser fakeUser() {
KumiteUserRawRaw rawRaw = KumiteUserRawRaw.builder().providerId("fakeProviderId").sub("fakeSub").build();
public static KumiteUser user() {
KumiteUserRawRaw rawRaw = KumiteUserRawRaw.builder().providerId("kumite").sub("fakeSub").build();
KumiteUserRaw raw = KumiteUserRaw.builder()
.rawRaw(rawRaw)
.username("fakeUsername")
.email("fake@fake")
.name("Fake User")
.build();
return KumiteUser.builder().accountId(FAKE_ACCOUNT_ID).playerId(FAKE_PLAYER_ID1).raw(raw).build();
return KumiteUser.builder().accountId(ACCOUNT_ID).playerId(PLAYER_ID1).raw(raw).build();
}

public static KumitePlayer fakePlayer() {
return KumitePlayer.builder().playerId(FAKE_PLAYER_ID1).accountId(FAKE_ACCOUNT_ID).build();
return KumitePlayer.builder().playerId(PLAYER_ID1).accountId(ACCOUNT_ID).build();
}

public static KumitePlayer fakePlayer(int i) {
return KumitePlayer.builder().playerId(fakePlayerId(i)).accountId(FAKE_ACCOUNT_ID).build();
return KumitePlayer.builder().playerId(fakePlayerId(i)).accountId(ACCOUNT_ID).build();
}

public static Set<UUID> fakePlayers() {
return Set.of(FakePlayerTokens.FAKE_PLAYER_ID1, FakePlayerTokens.FAKE_PLAYER_ID2);
return Set.of(FakePlayer.PLAYER_ID1, FakePlayer.PLAYER_ID2);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package eu.solven.kumite.account.fake_player;

import java.util.Set;
import java.util.UUID;

import eu.solven.kumite.account.KumiteUser;
import eu.solven.kumite.account.KumiteUserRaw;
import eu.solven.kumite.account.KumiteUserRawRaw;
import eu.solven.kumite.player.KumitePlayer;
import lombok.extern.slf4j.Slf4j;

/**
* Various tools specific to the RandomPlayer. This player is useful to generate activity even for a PRD contest-server,
* circumventing the need for an actual login flow, with an external login provider.
*
* @author Benoit Lacelle
*
*/
@Slf4j
public class RandomPlayer {

public static final UUID ACCOUNT_ID = UUID.fromString("FFFFFFFF-FFFF-FFFF-FFFF-000000000000");
public static final UUID PLAYERID_1 = UUID.fromString("FFFFFFFF-FFFF-FFFF-FFFF-111111111111");
public static final UUID RANDOM_PLAYERID2 = UUID.fromString("FFFFFFFF-FFFF-FFFF-FFFF-222222222222");

public static UUID randomPlayerId(int playerIndex) {
if (playerIndex == 0) {
return PLAYERID_1;
} else if (playerIndex == 1) {
return RANDOM_PLAYERID2;
} else {
throw new IllegalArgumentException("There is no randomPlayer for playerIndex=" + playerIndex);
}
}

public static boolean isRandomPlayer(UUID playerId) {
if (PLAYERID_1.equals(playerId) || RANDOM_PLAYERID2.equals(playerId)) {
return true;
} else {
return false;
}
}

public static KumiteUser user() {
KumiteUserRawRaw rawRaw = KumiteUserRawRaw.builder().providerId("kumite").sub("randomSub").build();
KumiteUserRaw raw = KumiteUserRaw.builder()
.rawRaw(rawRaw)
.username("randomUsername")
.email("random@random")
.name("Random User")
.build();
return KumiteUser.builder().accountId(ACCOUNT_ID).playerId(PLAYERID_1).raw(raw).build();
}

public static KumitePlayer randomPlayer() {
return KumitePlayer.builder().playerId(PLAYERID_1).accountId(ACCOUNT_ID).build();
}

public static KumitePlayer randomPlayer(int i) {
return KumitePlayer.builder().playerId(randomPlayerId(i)).accountId(ACCOUNT_ID).build();
}

public static Set<UUID> randomPlayers() {
return Set.of(RandomPlayer.PLAYERID_1, RandomPlayer.RANDOM_PLAYERID2);
}

}
Original file line number Diff line number Diff line change
@@ -77,4 +77,9 @@ public Stream<Map.Entry<UUID, ContestCreationMetadata>> stream() {
.map(contestId -> Map.entry(contestId, getById(contestId).orElse(ContestCreationMetadata.empty())))
.filter(e -> !e.getValue().getGameId().equals(IGameMetadataConstants.EMPTY));
}

@Override
public void archive(UUID contestId) {
// Do not remove, as TTL will do its job in due-time
}
}
Original file line number Diff line number Diff line change
@@ -8,10 +8,10 @@

import eu.solven.kumite.account.IKumiteUserRawRawRepository;
import eu.solven.kumite.account.IKumiteUserRepository;
import eu.solven.kumite.account.InMemoryUserRepository;
import eu.solven.kumite.account.KumiteUser;
import eu.solven.kumite.account.KumiteUserRaw;
import eu.solven.kumite.account.KumiteUserRawRaw;
import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.player.IAccountPlayersRegistry;
import eu.solven.kumite.player.KumitePlayer;
import eu.solven.kumite.redis.RepositoryKey;
@@ -103,11 +103,8 @@ public KumiteUser registerOrUpdate(KumiteUserRaw kumiteUserRaw) {
return getUser(rawRaw).orElseThrow(() -> new IllegalStateException("No user through we just registered one"));
}

private UUID generateAccountId(KumiteUserRawRaw rawRaw) {
if (rawRaw.equals(FakePlayerTokens.fakeUser().getRaw().getRawRaw())) {
return FakePlayerTokens.FAKE_ACCOUNT_ID;
}
return uuidGenerator.randomUUID();
protected UUID generateAccountId(KumiteUserRawRaw rawRaw) {
return InMemoryUserRepository.generateAccountId(uuidGenerator, rawRaw);
}

}
5 changes: 0 additions & 5 deletions server/pom.xml
Original file line number Diff line number Diff line change
@@ -39,11 +39,6 @@
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

<!-- tools -->
<dependency>
<groupId>org.springframework.boot</groupId>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package eu.solven.kumite.app;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import eu.solven.kumite.account.KumiteUsersRegistry;
import eu.solven.kumite.account.fake_player.FakePlayer;
import eu.solven.kumite.account.fake_player.RandomPlayer;
import eu.solven.kumite.player.IAccountPlayersRegistry;
import lombok.extern.slf4j.Slf4j;

@Configuration
@Slf4j
public class InjectKumiteAccountsConfig {

@Profile(IKumiteSpringProfiles.P_FAKEUSER)
@Bean
public Void initFakePlayer(KumiteUsersRegistry usersRegistry, IAccountPlayersRegistry accountPlayersRegistry) {
log.info("Registering the {} account and players", IKumiteSpringProfiles.P_FAKEUSER);

usersRegistry.registerOrUpdate(FakePlayer.user().getRaw());
accountPlayersRegistry.registerPlayer(FakePlayer.fakePlayer(1));

return null;
}

@Bean
public Void initRandomPlayer(KumiteUsersRegistry usersRegistry, IAccountPlayersRegistry accountPlayersRegistry) {
log.info("Registering the random account and players");

usersRegistry.registerOrUpdate(RandomPlayer.user().getRaw());
accountPlayersRegistry.registerPlayer(RandomPlayer.randomPlayer(1));

return null;
}
}
Original file line number Diff line number Diff line change
@@ -6,10 +6,8 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;

import eu.solven.kumite.account.KumiteUsersRegistry;
import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.app.persistence.InMemoryKumiteConfiguration;
import eu.solven.kumite.app.persistence.RedisKumiteConfiguration;
import eu.solven.kumite.board.BoardHandler;
@@ -45,6 +43,7 @@
PlayerMovesHandler.class,

InjectDefaultGamesConfig.class,
InjectKumiteAccountsConfig.class,

ContendersFromBoard.class,

@@ -63,14 +62,4 @@ public BoardLifecycleManager boardLifecycleManager(BoardsRegistry boardRegistry,

return new BoardLifecycleManager(boardRegistry, contestPlayersRegistry, boardEvolutionExecutor);
}

@Profile(IKumiteSpringProfiles.P_FAKEUSER)
@Bean
public Void initFakePlayer(KumiteUsersRegistry usersRegistry) {
log.info("Registering the {} account and players", IKumiteSpringProfiles.P_FAKEUSER);

usersRegistry.registerOrUpdate(FakePlayerTokens.fakeUser().getRaw());

return null;
}
}
Original file line number Diff line number Diff line change
@@ -77,9 +77,10 @@ public RouterFunction<ServerResponse> apiRoutes(PlayerVerifierFilterFunction pla
.GET(json("/contests"),
contestSearchHandler::listContests,
ops -> ops.operationId("searchContest").parameter(gameId))
.POST(json("/contests"),
contestSearchHandler::generateContest,
ops -> ops.operationId("publishContest"))
.POST(json("/contests"), contestSearchHandler::openContest, ops -> ops.operationId("publishContest"))
.DELETE(json("/contests"),
contestSearchHandler::openContest,
ops -> ops.operationId("deleteContest").parameter(contestId))

.GET(json("/board"),
boardHandler::getBoard,
@@ -114,8 +115,6 @@ public RouterFunction<ServerResponse> apiRoutes(PlayerVerifierFilterFunction pla
// webhooksHandler::dropWebhooks,
// ops -> ops.operationId("deleteWebhook"))

.filter(playerVerifierFilterFunction, ops -> {
})
.build();

}
Original file line number Diff line number Diff line change
@@ -15,7 +15,11 @@
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.user.OAuth2User;
@@ -30,14 +34,14 @@
import eu.solven.kumite.account.KumiteUser;
import eu.solven.kumite.account.KumiteUserRawRaw;
import eu.solven.kumite.account.KumiteUsersRegistry;
import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.account.login.IKumiteTestConstants;
import eu.solven.kumite.app.IKumiteSpringProfiles;
import eu.solven.kumite.oauth2.authorizationserver.KumiteTokenService;
import eu.solven.kumite.player.IAccountPlayersRegistry;
import eu.solven.kumite.player.KumitePlayer;
import eu.solven.kumite.security.LoginRouteButNotAuthenticatedException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;

/**
@@ -49,6 +53,7 @@
@RestController
@RequestMapping("/api/login/v1")
@AllArgsConstructor
@Slf4j
public class KumiteLoginController {
public static final String PROVIDERID_GITHUB = "github";
@Deprecated
@@ -88,12 +93,12 @@ public class KumiteLoginController {
@GetMapping("/html")
public ResponseEntity<?> loginpage(@AuthenticationPrincipal OAuth2User oauth2User) {
String url;
if (env.acceptsProfiles(Profiles.of(IKumiteSpringProfiles.P_FAKEUSER))) {
if (oauth2User != null) {
url = "login?success";
} else if (env.acceptsProfiles(Profiles.of(IKumiteSpringProfiles.P_FAKEUSER))) {
url = "login?" + IKumiteSpringProfiles.P_FAKEUSER;
} else if (oauth2User == null) {
url = "login";
} else {
url = "login?success";
url = "login";
}

// Spring-OAuth2-Login returns FOUND in case current user is not authenticated: let's follow this choice is=n
@@ -103,19 +108,26 @@ public ResponseEntity<?> loginpage(@AuthenticationPrincipal OAuth2User oauth2Use

// @PreAuthorize("isAuthenticated()")
@GetMapping("/user")
public Mono<KumiteUser> user(@AuthenticationPrincipal Mono<OAuth2User> oauth2User) {
if (env.acceptsProfiles(Profiles.of(IKumiteSpringProfiles.P_FAKEUSER))) {
return Mono.just(usersRegistry.getUser(FakePlayerTokens.FAKE_ACCOUNT_ID));
} else if (oauth2User == null) {
// Happens if this route is called without authentication
return Mono.error(() -> new LoginRouteButNotAuthenticatedException("Lack of OAuth2 user"));
} else {
return oauth2User.map(o -> {
KumiteUser user = userFromOAuth2(o);
public Mono<KumiteUser> user() {
return ReactiveSecurityContextHolder.getContext().map(sc -> {
Authentication authentication = sc.getAuthentication();

if (authentication instanceof UsernamePasswordAuthenticationToken usernameToken) {
// This happens on BASIC auth (for fakePlayer)
return userFromUsername(usernameToken);
} else if (authentication instanceof OAuth2AuthenticationToken oauth2Token) {
// This happens on OAuth2 auth (e.g. Github login)
return userFromOAuth2(oauth2Token.getPrincipal());
} else {
throw new LoginRouteButNotAuthenticatedException("Lack of authentication");
}
}).switchIfEmpty(Mono.error(() -> new LoginRouteButNotAuthenticatedException("No user")));
}

return user;
}).switchIfEmpty(Mono.error(() -> new LoginRouteButNotAuthenticatedException("No user")));
}
private KumiteUser userFromUsername(UsernamePasswordAuthenticationToken usernameToken) {
UUID accountId = UUID.fromString(usernameToken.getName());
KumiteUser user = usersRegistry.getUser(accountId);
return user;
}

private KumiteUser userFromOAuth2(OAuth2User o) {
@@ -152,10 +164,9 @@ private String guessProviderId(OAuth2User o) {
}

@GetMapping("/oauth2/token")
public Mono<?> token(@AuthenticationPrincipal Mono<OAuth2User> oauth2User,
@RequestParam(name = "player_id", required = false) String rawPlayerId,
public Mono<?> token(@RequestParam(name = "player_id", required = false) String rawPlayerId,
@RequestParam(name = "refresh_token", defaultValue = "false") boolean requestRefreshToken) {
return user(oauth2User).map(user -> {
return user().map(user -> {
if (requestRefreshToken) {
// TODO Restrict if `rawPlayerId` is provided.
if (!StringUtils.isEmpty(rawPlayerId)) {
@@ -164,11 +175,13 @@ public Mono<?> token(@AuthenticationPrincipal Mono<OAuth2User> oauth2User,
List<KumitePlayer> players = playersRegistry.makeDynamicHasPlayers(user.getAccountId()).getPlayers();
// Beware this would not allow playerIds generated after the refresh_token creation
Set<UUID> playerIds = players.stream().map(KumitePlayer::getPlayerId).collect(Collectors.toSet());
log.info("Generating an refresh_token for accountId={} playerIds={}", user.getAccountId(), playerIds);
return kumiteTokenService.wrapInJwtRefreshToken(user, playerIds);
} else {
UUID playerId =
KumiteHandlerHelper.optUuid(Optional.ofNullable(rawPlayerId)).orElse(user.getPlayerId());
checkValidPlayerId(user, playerId);
log.info("Generating an access_token for accountId={} playerId={}", user.getAccountId(), playerId);
return kumiteTokenService.wrapInJwtAccessToken(user, playerId);
}
});
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.WebExceptionHandler;

import eu.solven.kumite.account.JwtUserContextHolder;
import eu.solven.kumite.app.IKumiteSpringProfiles;
import eu.solven.kumite.app.webflux.KumiteWebExceptionHandler;
import eu.solven.kumite.app.webflux.api.KumiteLoginController;
@@ -30,6 +31,8 @@
KumiteResourceServerConfiguration.class,
KumiteTokenService.class,

JwtUserContextHolder.class,

})
@Slf4j
public class KumiteSecurity {
@@ -68,10 +71,10 @@ public Void checkSpringProfilesConsistency(Environment env) {
return null;
}

// @Bean
// WebFilter kumiteExceptionRoutingWebFilter() {
// return new KumiteExceptionRoutingWebFilter();
// }
// @Bean
// WebFilter kumiteExceptionRoutingWebFilter() {
// return new KumiteExceptionRoutingWebFilter();
// }

@Bean
WebExceptionHandler kumiteWebExceptionHandler() {
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package eu.solven.kumite.security;

import java.net.URI;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Bean;
@@ -10,19 +12,25 @@
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.security.oauth2.server.resource.web.server.BearerTokenServerAuthenticationEntryPoint;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;
import org.springframework.security.web.server.authentication.logout.RedirectServerLogoutSuccessHandler;
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
import org.springframework.security.web.server.csrf.WebSessionServerCsrfTokenRepository;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;

import com.nimbusds.jwt.JWT;

import eu.solven.kumite.account.fake_player.FakePlayer;
import eu.solven.kumite.app.IKumiteSpringProfiles;
import eu.solven.kumite.oauth2.resourceserver.KumiteResourceServerConfiguration;
import eu.solven.kumite.security.oauth2.KumiteOAuth2UserService;
@@ -47,11 +55,13 @@ public class SocialWebFluxSecurity {
public SecurityWebFilterChain configureUi(ServerProperties serverProperties,
ServerHttpSecurity http,
Environment env) {
boolean isSsl = serverProperties.getSsl() != null && serverProperties.getSsl().isEnabled();

ReactiveAuthenticationManager ram = auth -> {
throw new IllegalStateException();
};
boolean isFakeUser = env.acceptsProfiles(Profiles.of(IKumiteSpringProfiles.P_FAKEUSER));
if (isFakeUser) {
log.warn("{}=true", IKumiteSpringProfiles.P_FAKEUSER);
} else {
log.info("{}=false", IKumiteSpringProfiles.P_FAKEUSER);
}

return http
// We restrict the scope of this UI securityFilterChain to UI routes
@@ -123,7 +133,11 @@ public SecurityWebFilterChain configureUi(ServerProperties serverProperties,

// `/html/login` has to be synced with the SPA login route
.formLogin(login -> {
String loginPage = "/html/login".formatted(isSsl ? "s" : "");
ReactiveAuthenticationManager ram = auth -> {
throw new IllegalStateException();
};

String loginPage = "/html/login";
login.loginPage(loginPage)
// Required not to get an NPE at `.build()`
.authenticationManager(ram);
@@ -132,10 +146,34 @@ public SecurityWebFilterChain configureUi(ServerProperties serverProperties,
// https://docs.spring.io/spring-security/reference/servlet/oauth2/client/authorization-grants.html
// https://stackoverflow.com/questions/74242738/how-to-logout-from-oauth-signed-in-web-app-with-github
.oauth2Login(oauth2 -> {
String loginSuccess = "/html/login?success".formatted(isSsl ? "s" : "");
String loginSuccess = "/html/login?success";
oauth2.authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler(loginSuccess));
})

.httpBasic(basic -> {
Map<String, UserDetails> userDetails = new ConcurrentHashMap<>();

UserDetails fakeUser = User.builder()
.username(FakePlayer.ACCOUNT_ID.toString())
// `{noop}` relates with `PasswordEncoderFactories.createDelegatingPasswordEncoder()`
.password("{noop}" + "no_password")
.roles(IKumiteSpringProfiles.P_FAKEUSER)
.build();

userDetails.put(fakeUser.getUsername(), fakeUser);

UserDetailsRepositoryReactiveAuthenticationManager ram =
new UserDetailsRepositoryReactiveAuthenticationManager(
new MapReactiveUserDetailsService(userDetails));

if (isFakeUser) {
basic.authenticationManager(ram)
.securityContextRepository(new WebSessionServerSecurityContextRepository());
} else {
basic.disable();
}
})

.logout(logout -> {
RedirectServerLogoutSuccessHandler logoutSuccessHandler = new RedirectServerLogoutSuccessHandler();
// We need to redirect to a 2XX URL, and not a 3XX URL, as Fetch API can not intercept redirections.
@@ -161,8 +199,8 @@ public SecurityWebFilterChain configureApi(ServerHttpSecurity http,
Environment env,
ReactiveJwtDecoder jwtDecoder) {

boolean fakeUser = env.acceptsProfiles(Profiles.of(IKumiteSpringProfiles.P_FAKEUSER));
if (fakeUser) {
boolean isFakeUser = env.acceptsProfiles(Profiles.of(IKumiteSpringProfiles.P_FAKEUSER));
if (isFakeUser) {
log.warn("{}=true", IKumiteSpringProfiles.P_FAKEUSER);
} else {
log.info("{}=false", IKumiteSpringProfiles.P_FAKEUSER);
@@ -195,7 +233,7 @@ public SecurityWebFilterChain configureApi(ServerHttpSecurity http,
.permitAll()

// If fakeUser==true, we allow the reset route (for integration tests)
.pathMatchers(fakeUser ? "/api/v1/clear" : "nonono")
.pathMatchers(isFakeUser ? "/api/v1/clear" : "nonono")
.permitAll()

// The rest needs to be authenticated
Original file line number Diff line number Diff line change
@@ -48,8 +48,8 @@ public static void main(String[] args) {

@Bean
public Void generateFakePlayerToken(KumiteTokenService tokenService) {
String accessToken = tokenService.generateAccessToken(FakePlayerTokens.fakeUser(),
Set.of(FakePlayerTokens.FAKE_PLAYER_ID1, FakePlayerTokens.FAKE_PLAYER_ID2),
String accessToken = tokenService.generateAccessToken(FakePlayer.user(),
Set.of(FakePlayer.PLAYER_ID1, FakePlayer.PLAYER_ID2),
Duration.ofDays(365),
false);

Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;

import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.account.fake_player.FakePlayer;
import eu.solven.kumite.app.IKumiteSpringProfiles;
import eu.solven.kumite.app.KumiteContestServerApplication;
import eu.solven.kumite.app.webflux.KumiteWebExceptionHandler;
@@ -46,8 +46,8 @@ public class TestKumiteApiRouter {
KumiteTokenService tokenService;

protected String generateAccessToken() {
return tokenService.generateAccessToken(FakePlayerTokens.fakeUser(),
Set.of(FakePlayerTokens.FAKE_PLAYER_ID1),
return tokenService.generateAccessToken(FakePlayer.user(),
Set.of(FakePlayer.PLAYER_ID1),
Duration.ofMinutes(1),
false);
}
Original file line number Diff line number Diff line change
@@ -5,13 +5,17 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import eu.solven.kumite.account.JwtUserContextHolder;
import eu.solven.kumite.app.KumiteServerComponentsConfiguration;
import eu.solven.kumite.app.webflux.KumiteWebFluxConfiguration;
import eu.solven.kumite.app.webflux.api.GreetingHandler;
import lombok.extern.slf4j.Slf4j;

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = { KumiteWebFluxConfiguration.class, KumiteServerComponentsConfiguration.class, },
@SpringBootTest(
classes = { KumiteWebFluxConfiguration.class,
KumiteServerComponentsConfiguration.class,
JwtUserContextHolder.class, },
webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@Slf4j
public class TestKumiteRouterSpringConfig {
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@

import eu.solven.kumite.account.InMemoryUserRepository;
import eu.solven.kumite.account.KumiteUsersRegistry;
import eu.solven.kumite.app.InjectKumiteAccountsConfig;
import eu.solven.kumite.app.webflux.PlayerVerifierFilterFunction;
import eu.solven.kumite.app.webflux.api.AccessTokenHandler;
import eu.solven.kumite.app.webflux.api.GreetingHandler;
@@ -40,6 +41,8 @@
// IAccountPlayersRegistry is needed as security often checks the players of an account
BijectiveAccountPlayersRegistry.class,

InjectKumiteAccountsConfig.class,

})
public class KumiteServerSecurityApplication {

Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package eu.solven.kumite.app.it.security;

import java.nio.charset.StandardCharsets;
import java.util.Map;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import eu.solven.kumite.account.fake_player.FakePlayer;
import eu.solven.kumite.app.IKumiteSpringProfiles;
import eu.solven.kumite.app.KumiteJackson;
import eu.solven.kumite.app.webflux.api.KumiteLoginController;
import eu.solven.kumite.login.AccessTokenWrapper;
import lombok.extern.slf4j.Slf4j;

/**
* OAuth2 enables logging-in a subset of APIs, especially the login APIs.
*
* @author Benoit Lacelle
*
*/
@ExtendWith(SpringExtension.class)
@ActiveProfiles({ IKumiteSpringProfiles.P_UNSAFE, IKumiteSpringProfiles.P_FAKEUSER })
@Slf4j
public class TestSecurity_WithFakeUser extends TestSecurity_WithOAuth2User {

@Test
@Override
public void testLoginAccessToken() {
log.debug("About {}", KumiteLoginController.class);

webTestClient

.get()
.uri("/api/login/v1/oauth2/token")
.accept(MediaType.APPLICATION_JSON)
.header(HttpHeaders.AUTHORIZATION,
"Basic " + HttpHeaders.encodeBasicAuth(FakePlayer.ACCOUNT_ID.toString(),
"no_password",
StandardCharsets.UTF_8))
.exchange()

.expectStatus()
.isOk()
.expectBody(AccessTokenWrapper.class)
.value(token -> {
Map asMap = KumiteJackson.objectMapper().convertValue(token, Map.class);

Assertions.assertThat(asMap)
.containsKey("access_token")
.containsEntry("token_type", "Bearer")
.containsEntry("player_id", FakePlayer.PLAYER_ID1.toString())
.containsEntry("expires_in", 3600L)
.hasSize(4);
});
}

@Test
public void testLoginAccessToken_invalidUser() {
log.debug("About {}", KumiteLoginController.class);

webTestClient

.get()
.uri("/api/login/v1/oauth2/token")
.accept(MediaType.APPLICATION_JSON)
.header(HttpHeaders.AUTHORIZATION,
"Basic " + HttpHeaders
.encodeBasicAuth("someUnknownUser", "no_password", StandardCharsets.UTF_8))
.exchange()

.expectStatus()
.isUnauthorized();
}
}
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@
import org.springframework.test.web.reactive.server.StatusAssertions;
import org.springframework.test.web.reactive.server.WebTestClient;

import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.account.fake_player.RandomPlayer;
import eu.solven.kumite.app.IKumiteSpringProfiles;
import eu.solven.kumite.app.webflux.KumiteWebExceptionHandler;
import eu.solven.kumite.app.webflux.api.AccessTokenHandler;
@@ -56,15 +56,15 @@ public class TestSecurity_WithJwtUser {
KumiteTokenService tokenService;

protected String generateAccessToken() {
return tokenService.generateAccessToken(FakePlayerTokens.fakeUser(),
Set.of(FakePlayerTokens.FAKE_PLAYER_ID1),
return tokenService.generateAccessToken(RandomPlayer.user(),
Set.of(RandomPlayer.PLAYERID_1),
Duration.ofMinutes(1),
false);
}

protected String generateRefreshToken() {
return tokenService.generateAccessToken(FakePlayerTokens.fakeUser(),
Set.of(FakePlayerTokens.FAKE_PLAYER_ID1),
return tokenService.generateAccessToken(RandomPlayer.user(),
Set.of(RandomPlayer.PLAYERID_1),
Duration.ofMinutes(1),
true);
}
@@ -274,7 +274,7 @@ public void testMakeRefreshToken() {

// We need an oauth2 user, not a jwt user
expectStatus.isUnauthorized().expectBody(Map.class).value(bodyAsMap -> {
Assertions.assertThat(bodyAsMap).containsEntry("error_message", "Lack of OAuth2 user").hasSize(1);
Assertions.assertThat(bodyAsMap).containsEntry("error_message", "No user").hasSize(1);
});

}
@@ -285,7 +285,7 @@ public void testRefreshTokenToAccessToken() {
log.debug("About {}", AccessTokenHandler.class);

StatusAssertions expectStatus = webTestClient.get()
.uri("/api/v1/oauth2/token?player_id=11111111-1111-1111-1111-111111111111")
.uri("/api/v1/oauth2/token?player_id=" + RandomPlayer.PLAYERID_1)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + generateRefreshToken())
.accept(MediaType.APPLICATION_JSON)
.exchange()
Original file line number Diff line number Diff line change
@@ -49,14 +49,15 @@
@ActiveProfiles({ IKumiteSpringProfiles.P_UNSAFE, })
@Slf4j
// https://stackoverflow.com/questions/73881370/mocking-oauth2-client-with-webtestclient-for-servlet-applications-results-in-nul
@AutoConfigureWebTestClient
// https://stackoverflow.com/questions/56784289/autoconfigurewebtestclienttimeout-600000-has-no-effect
@AutoConfigureWebTestClient(timeout = "PT10M")
@WithMockUser
public class TestSecurity_WithOAuth2User {

// Spring Boot will create a `WebTestClient` for you,
// already configure and ready to issue requests against "localhost:RANDOM_PORT"
@Autowired
private WebTestClient webTestClient;
WebTestClient webTestClient;

@Autowired
KumiteOAuth2UserService oauth2UserService;
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@

import eu.solven.kumite.account.InMemoryUserRepository;
import eu.solven.kumite.account.KumiteUsersRegistry;
import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.account.fake_player.FakePlayer;
import eu.solven.kumite.oauth2.IKumiteOAuth2Constants;
import eu.solven.kumite.oauth2.authorizationserver.KumiteTokenService;
import eu.solven.kumite.player.IAccountPlayersRegistry;
@@ -54,7 +54,7 @@ public class TestKumiteLoginController {
public void testPlayer_invalid() {
Assertions
.assertThatThrownBy(
() -> controller.checkValidPlayerId(FakePlayerTokens.fakeUser(), uuidGenerator.randomUUID()))
() -> controller.checkValidPlayerId(FakePlayer.user(), uuidGenerator.randomUUID()))
.isInstanceOf(IllegalArgumentException.class);
}
}
Original file line number Diff line number Diff line change
@@ -6,20 +6,53 @@
import org.junit.jupiter.api.Test;

import eu.solven.kumite.account.InMemoryUserRepository;
import eu.solven.kumite.account.KumiteUser;
import eu.solven.kumite.account.KumiteUserRawRaw;
import eu.solven.kumite.account.fake_player.FakePlayerTokens;
import eu.solven.kumite.account.fake_player.FakePlayer;
import eu.solven.kumite.account.fake_player.RandomPlayer;
import eu.solven.kumite.account.login.IKumiteTestConstants;
import eu.solven.kumite.player.persistence.BijectiveAccountPlayersRegistry;
import eu.solven.kumite.tools.JdkUuidGenerator;

public class TestInMemoryUserRepository {
BijectiveAccountPlayersRegistry playersRegistry = new BijectiveAccountPlayersRegistry();
InMemoryUserRepository userRepository = new InMemoryUserRepository(JdkUuidGenerator.INSTANCE, playersRegistry);

@Test
public void testFakeAccount() {
InMemoryUserRepository userRepository = InMemoryUserRepository.forTests(playersRegistry);
public void testRegisterUser() {
KumiteUser user = userRepository.registerOrUpdate(IKumiteTestConstants.userRaw());

Optional<KumiteUserRawRaw> optRawRaw = userRepository.getUser(FakePlayerTokens.FAKE_ACCOUNT_ID);
Optional<KumiteUserRawRaw> optRawRaw = userRepository.getUser(user.getAccountId());
Assertions.assertThat(optRawRaw).isPresent().contains(user.getRaw().getRawRaw());
}

Assertions.assertThat(optRawRaw).isPresent();
@Test
public void testFakeUser() {
// Not present by default
{
Optional<KumiteUserRawRaw> optRawRaw = userRepository.getUser(FakePlayer.ACCOUNT_ID);
Assertions.assertThat(optRawRaw).isEmpty();
}

KumiteUser user = userRepository.registerOrUpdate(FakePlayer.user().getRaw());
Assertions.assertThat(user.getAccountId()).isEqualTo(FakePlayer.ACCOUNT_ID);

Optional<KumiteUserRawRaw> optRawRaw = userRepository.getUser(user.getAccountId());
Assertions.assertThat(optRawRaw).isPresent().contains(user.getRaw().getRawRaw());
}

@Test
public void testRandomUser() {
// Not present by default
{
Optional<KumiteUserRawRaw> optRawRaw = userRepository.getUser(RandomPlayer.ACCOUNT_ID);
Assertions.assertThat(optRawRaw).isEmpty();
}

KumiteUser user = userRepository.registerOrUpdate(RandomPlayer.user().getRaw());
Assertions.assertThat(user.getAccountId()).isEqualTo(RandomPlayer.ACCOUNT_ID);

Optional<KumiteUserRawRaw> optRawRaw = userRepository.getUser(user.getAccountId());
Assertions.assertThat(optRawRaw).isPresent().contains(user.getRaw().getRawRaw());
}
}
6 changes: 6 additions & 0 deletions tools/pom.xml
Original file line number Diff line number Diff line change
@@ -19,6 +19,12 @@
<version>5.1.0</version>
</dependency>

<dependency>
<!-- Used by IKumiteUserContextHolder (Mono) -->
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>

<dependency>
<!-- Used by CloseableBean (DisposableBean) -->
<groupId>org.springframework</groupId>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package eu.solven.kumite.account;

import java.util.UUID;

import reactor.core.publisher.Mono;

/**
* Give access to the authenticated user.
*
* @author Benoit Lacelle
*
*/
public interface IKumiteUserContextHolder {
Mono<UUID> authenticatedAccountId();
}

0 comments on commit 2ac66ce

Please sign in to comment.