Added dev impersonation and changed api a little bit

This commit is contained in:
Andreas Dahm committed 2026-04-29 08:50:02 +02:00
1 parent 1b08e7902c
commit ac97442312
8 files changed
+104 -18

No files matched your search

@@ -0,0 +1,51 @@
package core_service.configuration.security;
import core_service.shared.security.UserPrincipal;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.jspecify.annotations.NonNull;
import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.Collections;
/**
* Development-specific authentication filter that bypasses real JWT validation.
* It automatically injects a default user principal for every request when the 'dev' profile is active.
*/
@Component("jwtAuthenticationFilter")
@Profile("dev")
@Slf4j
public class DevJwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {
if (SecurityContextHolder.getContext().getAuthentication() == null) {
// Default user principal for dev mode: Using the requested Steam ID 76561198000000001
UserPrincipal principal = new UserPrincipal("1", "dev@example.com", 76561198000000001L);
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
principal,
null,
Collections.emptyList()
);
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
log.trace("Dev mode: Injected default user principal with SteamID: {}", principal.steamId());
}
filterChain.doFilter(request, response);
}
}
@@ -8,6 +8,7 @@ import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jspecify.annotations.NonNull;
import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
@@ -17,7 +18,8 @@ import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.Collections;
@Component
@Component("jwtAuthenticationFilter")
@Profile("!dev")
@RequiredArgsConstructor
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@@ -4,10 +4,12 @@ import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Getter
@Setter
@Configuration
@Profile("!dev")
@ConfigurationProperties(prefix = "security.jwt")
public class JwtConfig {
@@ -2,12 +2,14 @@ package core_service.configuration.security;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
@Service
@Profile("!dev")
public class JwtService {
private final JwtConfig jwtConfig;
@@ -1,6 +1,7 @@
package core_service.configuration.security;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -14,13 +15,13 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.OncePerRequestFilter;
import java.util.Arrays;
import java.util.List;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private static final String[] WHITE_LIST_URLS = {
@@ -31,7 +32,11 @@ public class SecurityConfig {
"/api/steam/test/**"
};
private final JwtAuthenticationFilter jwtAuthFilter;
private final OncePerRequestFilter jwtAuthFilter;
public SecurityConfig(@Qualifier("jwtAuthenticationFilter") OncePerRequestFilter jwtAuthFilter) {
this.jwtAuthFilter = jwtAuthFilter;
}
@Value("${app.cors.allowed-origins}")
private String[] allowedOrigins;
@@ -1,18 +1,24 @@
package core_service.modules.steam_adapter.api.dto.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public record SteamOwnedGamesResponse(OwnedGamesResponse response) {
@JsonIgnoreProperties(ignoreUnknown = true)
public record SteamOwnedGamesResponse(
@JsonProperty("response") OwnedGamesResponse response
) {
@JsonIgnoreProperties(ignoreUnknown = true)
public record OwnedGamesResponse(
@JsonProperty("game_count") Integer gameCount,
List<Game> games
@JsonProperty("games") List<Game> games
) {}
@JsonIgnoreProperties(ignoreUnknown = true)
public record Game(
Integer appid,
String name,
@JsonProperty("appid") Integer appid,
@JsonProperty("name") String name,
@JsonProperty("playtime_forever") Integer playtimeForever,
@JsonProperty("img_icon_url") String imgIconUrl,
@JsonProperty("has_community_visible_stats") Boolean hasCommunityVisibleStats
@@ -14,9 +14,9 @@ import java.util.Collections;
import java.util.List;
import java.util.Optional;
@Slf4j
@Service
@RequiredArgsConstructor
@Slf4j
public class SteamApiService {
private final RestClient steamRestClient;
@@ -26,24 +26,42 @@ public class SteamApiService {
@Cacheable(value = "steamGameCount", key = "#steamId")
public int getOwnedGamesCount(String steamId) {
if (apiKey == null || apiKey.isEmpty() || apiKey.equals("${STEAM_API_KEY}")) {
log.error("Steam API Key is not configured correctly!");
return 0;
}
try {
log.info("Fetching owned games count for SteamID: {}", steamId);
log.info("Fetching owned games count for SteamID: {} using API Key starting with: {}",
steamId, apiKey.substring(0, Math.min(apiKey.length(), 4)));
SteamOwnedGamesResponse response = steamRestClient.get()
.uri(uriBuilder -> uriBuilder
.path("/IPlayerService/GetOwnedGames/v0001/")
.path("/IPlayerService/GetOwnedGames/v1/")
.queryParam("key", apiKey)
.queryParam("steamid", steamId)
.queryParam("include_appinfo", 0)
.queryParam("include_played_free_games", 1)
.queryParam("include_appinfo", "true")
.queryParam("include_played_free_games", "true")
.queryParam("format", "json")
.build())
.retrieve()
.body(SteamOwnedGamesResponse.class);
log.info("SteamOwnedGamesResponse: {}", response);
log.info("Steam API Response object for {}: {}", steamId, response);
if (response != null && response.response() != null) {
return response.response().gameCount() != null ? response.response().gameCount() : 0;
Integer count = response.response().gameCount();
if (count == null) {
if (response.response().games() != null) {
int size = response.response().games().size();
log.info("game_count was null, but games list has {} entries for SteamID: {}", size, steamId);
return size;
}
log.warn("Steam API returned an empty response object for SteamID: {}. Check if the ID is correct and 'Game details' are Public.", steamId);
return 0;
}
log.info("Successfully fetched {} games for SteamID: {}", count, steamId);
return count;
}
} catch (Exception e) {
@@ -43,7 +43,7 @@ class SteamApiServiceTest {
}
""";
server.expect(requestTo(containsString("/IPlayerService/GetOwnedGames/v0001/")))
server.expect(requestTo(containsString("/IPlayerService/GetOwnedGames/v1/")))
.andExpect(queryParam("steamid", steamId))
.andExpect(queryParam("key", "test-api-key"))
.andRespond(withSuccess(jsonResponse, MediaType.APPLICATION_JSON));
@@ -102,7 +102,7 @@ class SteamApiServiceTest {
}
""";
server.expect(requestTo(containsString("/IPlayerService/GetOwnedGames/v0001/")))
server.expect(requestTo(containsString("/IPlayerService/GetOwnedGames/v1/")))
.andExpect(queryParam("steamid", steamId))
.andExpect(queryParam("key", "test-api-key"))
.andRespond(withSuccess(jsonResponse, MediaType.APPLICATION_JSON));
@@ -130,7 +130,7 @@ class SteamApiServiceTest {
}
""";
server.expect(requestTo(containsString("/IPlayerService/GetOwnedGames/v0001/")))
server.expect(requestTo(containsString("/IPlayerService/GetOwnedGames/v1/")))
.andRespond(withSuccess(jsonResponse, MediaType.APPLICATION_JSON));
List<SteamOwnedGamesResponse.Game> result = steamApiService.getOwnedGames(steamId);
@@ -178,7 +178,7 @@ class SteamApiServiceTest {
server.expect(requestTo(containsString("/ISteamUser/GetPlayerSummaries/v0002/")))
.andRespond(withSuccess(summaryJson, MediaType.APPLICATION_JSON));
server.expect(requestTo(containsString("/IPlayerService/GetOwnedGames/v0001/")))
server.expect(requestTo(containsString("/IPlayerService/GetOwnedGames/v1/")))
.andRespond(withSuccess(gamesJson, MediaType.APPLICATION_JSON));
Optional<SteamProfileAndGamesResponse> result = steamApiService.getFullPlayerProfile(steamId);