Public Access
Added dev impersonation and changed api a little bit
This commit is contained in:
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.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.jspecify.annotations.NonNull;
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
@@ -17,7 +18,8 @@ import org.springframework.web.filter.OncePerRequestFilter;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
@Component
|
@Component("jwtAuthenticationFilter")
|
||||||
|
@Profile("!dev")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import lombok.Getter;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@Profile("!dev")
|
||||||
@ConfigurationProperties(prefix = "security.jwt")
|
@ConfigurationProperties(prefix = "security.jwt")
|
||||||
public class JwtConfig {
|
public class JwtConfig {
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ package core_service.configuration.security;
|
|||||||
|
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
import io.jsonwebtoken.security.Keys;
|
import io.jsonwebtoken.security.Keys;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@Profile("!dev")
|
||||||
public class JwtService {
|
public class JwtService {
|
||||||
|
|
||||||
private final JwtConfig jwtConfig;
|
private final JwtConfig jwtConfig;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package core_service.configuration.security;
|
package core_service.configuration.security;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
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.CorsConfiguration;
|
||||||
import org.springframework.web.cors.CorsConfigurationSource;
|
import org.springframework.web.cors.CorsConfigurationSource;
|
||||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
private static final String[] WHITE_LIST_URLS = {
|
private static final String[] WHITE_LIST_URLS = {
|
||||||
@@ -31,7 +32,11 @@ public class SecurityConfig {
|
|||||||
"/api/steam/test/**"
|
"/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}")
|
@Value("${app.cors.allowed-origins}")
|
||||||
private String[] allowedOrigins;
|
private String[] allowedOrigins;
|
||||||
|
|||||||
+10
-4
@@ -1,18 +1,24 @@
|
|||||||
package core_service.modules.steam_adapter.api.dto.response;
|
package core_service.modules.steam_adapter.api.dto.response;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import java.util.List;
|
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(
|
public record OwnedGamesResponse(
|
||||||
@JsonProperty("game_count") Integer gameCount,
|
@JsonProperty("game_count") Integer gameCount,
|
||||||
List<Game> games
|
@JsonProperty("games") List<Game> games
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public record Game(
|
public record Game(
|
||||||
Integer appid,
|
@JsonProperty("appid") Integer appid,
|
||||||
String name,
|
@JsonProperty("name") String name,
|
||||||
@JsonProperty("playtime_forever") Integer playtimeForever,
|
@JsonProperty("playtime_forever") Integer playtimeForever,
|
||||||
@JsonProperty("img_icon_url") String imgIconUrl,
|
@JsonProperty("img_icon_url") String imgIconUrl,
|
||||||
@JsonProperty("has_community_visible_stats") Boolean hasCommunityVisibleStats
|
@JsonProperty("has_community_visible_stats") Boolean hasCommunityVisibleStats
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
public class SteamApiService {
|
public class SteamApiService {
|
||||||
|
|
||||||
private final RestClient steamRestClient;
|
private final RestClient steamRestClient;
|
||||||
@@ -26,24 +26,42 @@ public class SteamApiService {
|
|||||||
|
|
||||||
@Cacheable(value = "steamGameCount", key = "#steamId")
|
@Cacheable(value = "steamGameCount", key = "#steamId")
|
||||||
public int getOwnedGamesCount(String 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 {
|
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()
|
SteamOwnedGamesResponse response = steamRestClient.get()
|
||||||
.uri(uriBuilder -> uriBuilder
|
.uri(uriBuilder -> uriBuilder
|
||||||
.path("/IPlayerService/GetOwnedGames/v0001/")
|
.path("/IPlayerService/GetOwnedGames/v1/")
|
||||||
.queryParam("key", apiKey)
|
.queryParam("key", apiKey)
|
||||||
.queryParam("steamid", steamId)
|
.queryParam("steamid", steamId)
|
||||||
.queryParam("include_appinfo", 0)
|
.queryParam("include_appinfo", "true")
|
||||||
.queryParam("include_played_free_games", 1)
|
.queryParam("include_played_free_games", "true")
|
||||||
|
.queryParam("format", "json")
|
||||||
.build())
|
.build())
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.body(SteamOwnedGamesResponse.class);
|
.body(SteamOwnedGamesResponse.class);
|
||||||
|
|
||||||
log.info("SteamOwnedGamesResponse: {}", response);
|
log.info("Steam API Response object for {}: {}", steamId, response);
|
||||||
|
|
||||||
if (response != null && response.response() != null) {
|
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) {
|
} 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("steamid", steamId))
|
||||||
.andExpect(queryParam("key", "test-api-key"))
|
.andExpect(queryParam("key", "test-api-key"))
|
||||||
.andRespond(withSuccess(jsonResponse, MediaType.APPLICATION_JSON));
|
.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("steamid", steamId))
|
||||||
.andExpect(queryParam("key", "test-api-key"))
|
.andExpect(queryParam("key", "test-api-key"))
|
||||||
.andRespond(withSuccess(jsonResponse, MediaType.APPLICATION_JSON));
|
.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));
|
.andRespond(withSuccess(jsonResponse, MediaType.APPLICATION_JSON));
|
||||||
|
|
||||||
List<SteamOwnedGamesResponse.Game> result = steamApiService.getOwnedGames(steamId);
|
List<SteamOwnedGamesResponse.Game> result = steamApiService.getOwnedGames(steamId);
|
||||||
@@ -178,7 +178,7 @@ class SteamApiServiceTest {
|
|||||||
server.expect(requestTo(containsString("/ISteamUser/GetPlayerSummaries/v0002/")))
|
server.expect(requestTo(containsString("/ISteamUser/GetPlayerSummaries/v0002/")))
|
||||||
.andRespond(withSuccess(summaryJson, MediaType.APPLICATION_JSON));
|
.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));
|
.andRespond(withSuccess(gamesJson, MediaType.APPLICATION_JSON));
|
||||||
|
|
||||||
Optional<SteamProfileAndGamesResponse> result = steamApiService.getFullPlayerProfile(steamId);
|
Optional<SteamProfileAndGamesResponse> result = steamApiService.getFullPlayerProfile(steamId);
|
||||||
|
|||||||
Reference in new issue
Block a user