Skip to content

Commit 436a4b0

Browse files
Sheikah45Brutus5000
authored andcommitted
Add tests and remove async from getFeaturedModFiles for testability
1 parent 33dff8f commit 436a4b0

File tree

9 files changed

+59
-26
lines changed

9 files changed

+59
-26
lines changed
Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package com.faforever.api.featuredmods;
22

33
import com.faforever.api.AbstractIntegrationTest;
4+
import com.faforever.api.security.OAuthScope;
45
import org.junit.jupiter.api.Test;
56
import org.springframework.test.context.jdbc.Sql;
67

78
import static org.hamcrest.Matchers.hasSize;
89
import static org.hamcrest.Matchers.is;
10+
import static org.hamcrest.Matchers.matchesRegex;
911
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
1012
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
1113
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -16,11 +18,19 @@
1618
public class FeaturedModsControllerTest extends AbstractIntegrationTest {
1719

1820
@Test
19-
public void featuredModFileUrlCorrect() throws Exception {
21+
public void featuredModFileUrlCorrectWithLobbyScope() throws Exception {
2022
mockMvc.perform(get("/featuredMods/0/files/latest")
21-
.with(getOAuthTokenWithActiveUser(NO_SCOPE, NO_AUTHORITIES)))
22-
.andExpect(status().isOk())
23-
.andExpect(jsonPath("$.data", hasSize(1)))
24-
.andExpect(jsonPath("$.data[0].attributes.url", is("USER")));
23+
.with(getOAuthTokenWithActiveUser(OAuthScope._LOBBY, NO_AUTHORITIES)))
24+
.andExpect(status().isOk())
25+
.andExpect(jsonPath("$.data", hasSize(1)))
26+
.andExpect(jsonPath("$.data[0].type", is("featuredModFile")))
27+
.andExpect(jsonPath("$.data[0].attributes.url", matchesRegex(".*\\?verify=[0-9]+-.*")));
28+
}
29+
30+
@Test
31+
public void featuredModFileNotVisibleWithoutLobbyScope() throws Exception {
32+
mockMvc.perform(get("/featuredMods/0/files/latest")
33+
.with(getOAuthTokenWithActiveUser(NO_SCOPE, NO_AUTHORITIES)))
34+
.andExpect(status().isForbidden());
2535
}
2636
}

src/inttest/resources/config/application.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ faf-api:
5353
max-size-bytes: 4096
5454
image-width: 40
5555
image-height: 20
56+
cloudflare:
57+
hmac-secret: "banana"
58+
hmac-param: "verify"
5659
clan:
5760
website-url-format: "http://example.com/%s"
5861
tutorial:

src/inttest/resources/sql/prepFeaturedMods.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
-- game_featuredMods is populated by R__010_game_featuredMods.sql from Flyway
2+
INSERT INTO `updates_faf` (`id`, `filename`, `path`) VALUES
3+
(1, 'ForgedAlliance.exe', 'bin');
4+
5+
INSERT INTO `updates_faf_files` (`id`, `fileId`, `version`, `name`, `md5`, `obselete`) VALUES
6+
(1703, 1, 3706, 'ForgedAlliance.3706.exe', 'c20b922a785cf5876c39b7696a16f162', 0);
27

38
INSERT INTO `updates_fafbeta` (`id`, `filename`, `path`) VALUES
49
(1, 'ForgedAlliance.exe', 'bin');

src/main/java/com/faforever/api/config/FafApiProperties.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public class FafApiProperties {
2323
private Replay replay = new Replay();
2424
private Avatar avatar = new Avatar();
2525
private Clan clan = new Clan();
26+
private Cloudflare cloudflare = new Cloudflare();
2627
private FeaturedMod featuredMod = new FeaturedMod();
2728
private GitHub gitHub = new GitHub();
2829
private Deployment deployment = new Deployment();
@@ -137,7 +138,6 @@ public static class Avatar {
137138
@Data
138139
public static class FeaturedMod {
139140
private String fileUrlFormat;
140-
private String cloudflareHmacSecret;
141141
}
142142

143143
@Data
@@ -146,6 +146,12 @@ public static class Clan {
146146
private String websiteUrlFormat;
147147
}
148148

149+
@Data
150+
public static class Cloudflare {
151+
private String hmacParam;
152+
private String hmacSecret;
153+
}
154+
149155
@Data
150156
public static class GitHub {
151157
private String webhookSecret;

src/main/java/com/faforever/api/featuredmods/FeaturedModFileEnricher.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ public void init(FafApiProperties fafApiProperties) {
3232
public void enhance(FeaturedModFile featuredModFile) throws NoSuchAlgorithmException, InvalidKeyException {
3333
String folder = featuredModFile.getFolderName();
3434
String urlFormat = fafApiProperties.getFeaturedMod().getFileUrlFormat();
35-
String secret = fafApiProperties.getFeaturedMod().getCloudflareHmacSecret();
35+
String secret = fafApiProperties.getCloudflare().getHmacSecret();
3636
long timeStamp = Instant.now().getEpochSecond();
37-
URI featuredModUri = URI.create(String.format(urlFormat, folder, featuredModFile.getOriginalFileName()));
37+
URI featuredModUri = URI.create(urlFormat.formatted(folder, featuredModFile.getOriginalFileName()));
3838

3939
// Builds hmac token for cloudflare firewall verification as specified at
4040
// https://support.cloudflare.com/hc/en-us/articles/115001376488-Configuring-Token-Authentication
@@ -43,8 +43,9 @@ public void enhance(FeaturedModFile featuredModFile) throws NoSuchAlgorithmExcep
4343
byte[] macMessage = (featuredModUri.getPath() + timeStamp).getBytes(StandardCharsets.UTF_8);
4444

4545
String hmacEncoded = URLEncoder.encode(new String(Base64.getEncoder().encode(mac.doFinal(macMessage)), StandardCharsets.UTF_8), StandardCharsets.UTF_8);
46-
String parameter = "%d-%s".formatted(timeStamp, hmacEncoded);
46+
String parameterValue = "%d-%s".formatted(timeStamp, hmacEncoded);
4747

48-
featuredModFile.setUrl(UriComponentsBuilder.fromUri(featuredModUri).queryParam("verify", parameter).build().toString());
48+
String queryParam = fafApiProperties.getCloudflare().getHmacParam();
49+
featuredModFile.setUrl(UriComponentsBuilder.fromUri(featuredModUri).queryParam(queryParam, parameterValue).build().toString());
4950
}
5051
}

src/main/java/com/faforever/api/featuredmods/FeaturedModService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ public Map<String, Integer> getFileIds(String modName) {
4444
return legacyFeaturedModFileRepository.getFileIds(modName);
4545
}
4646

47+
public Optional<FeaturedMod> findModById(int id) {
48+
return featuredModRepository.findById(id);
49+
}
50+
4751
public Optional<FeaturedMod> findModByTechnicalName(String name) {
4852
return featuredModRepository.findOneByTechnicalName(name);
4953
}

src/main/java/com/faforever/api/featuredmods/FeaturedModsController.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package com.faforever.api.featuredmods;
22

33
import com.faforever.api.data.domain.FeaturedMod;
4+
import com.faforever.api.error.ApiException;
5+
import com.faforever.api.error.Error;
46
import com.faforever.api.security.OAuthScope;
5-
import com.google.common.collect.Maps;
67
import com.yahoo.elide.jsonapi.models.Data;
78
import com.yahoo.elide.jsonapi.models.JsonApiDocument;
89
import com.yahoo.elide.jsonapi.models.Resource;
910
import io.swagger.annotations.ApiOperation;
10-
import org.springframework.scheduling.annotation.Async;
1111
import org.springframework.security.access.prepost.PreAuthorize;
1212
import org.springframework.web.bind.annotation.PathVariable;
1313
import org.springframework.web.bind.annotation.RequestMapping;
@@ -17,9 +17,10 @@
1717
import java.util.List;
1818
import java.util.Map;
1919
import java.util.Optional;
20-
import java.util.concurrent.CompletableFuture;
2120
import java.util.function.Function;
2221

22+
import static com.faforever.api.error.ErrorCode.FEATURED_MOD_UNKNOWN;
23+
2324
@RestController
2425
@RequestMapping(path = "/featuredMods")
2526
public class FeaturedModsController {
@@ -30,28 +31,27 @@ public FeaturedModsController(FeaturedModService featuredModService) {
3031
this.featuredModService = featuredModService;
3132
}
3233

33-
@Async
3434
@RequestMapping(path = "/{modId}/files/{version}")
3535
@ApiOperation("Lists the required files for a specific featured mod version")
3636
@PreAuthorize("hasScope('" + OAuthScope._LOBBY + "')")
37-
public CompletableFuture<JsonApiDocument> getFiles(@PathVariable("modId") int modId,
38-
@PathVariable("version") String version,
39-
@RequestParam(value = "page[number]", required = false) Integer page) {
37+
public JsonApiDocument getFiles(@PathVariable("modId") int modId,
38+
@PathVariable("version") String version,
39+
@RequestParam(value = "page[number]", required = false) Integer page) {
4040
Integer innerPage = Optional.ofNullable(page).orElse(0);
4141
if (innerPage > 1) {
42-
return CompletableFuture.completedFuture(new JsonApiDocument(new Data<>(List.of())));
42+
return new JsonApiDocument(new Data<>(List.of()));
4343
}
4444

45-
Map<Integer, FeaturedMod> mods = Maps.uniqueIndex(featuredModService.getFeaturedMods(), FeaturedMod::getId);
46-
FeaturedMod featuredMod = mods.get(modId);
45+
FeaturedMod featuredMod = featuredModService.findModById(modId)
46+
.orElseThrow(() -> new ApiException(new Error(FEATURED_MOD_UNKNOWN, modId)));
4747

4848
Integer innerVersion = "latest".equals(version) ? null : Integer.valueOf(version);
4949

5050
List<Resource> values = featuredModService.getFiles(featuredMod.getTechnicalName(), innerVersion).stream()
51-
.map(modFileMapper())
52-
.toList();
51+
.map(modFileMapper())
52+
.toList();
5353

54-
return CompletableFuture.completedFuture(new JsonApiDocument(new Data<>(values)));
54+
return new JsonApiDocument(new Data<>(values));
5555
}
5656

5757
private Function<FeaturedModFile, Resource> modFileMapper() {

src/main/resources/config/application-local.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ faf-api:
2222
allowed-extensions: ${AVATAR_ALLOWED_FILE_EXTENSIONS:png}
2323
featured-mod:
2424
file-url-format: ${FEATURED_MOD_URL_FORMAT:https://localhost/legacy-featured-mod-files/%s/%s}
25-
cloudflare-hmac-secret: ${CLOUDFLARE_HMAC_SECRET:banana}
2625
git-hub:
2726
deployment-environment: ${GITHUB_DEPLOYMENT_ENVIRONMENT:development}
27+
cloudflare:
28+
hmac-secret: ${CLOUDFLARE_HMAC_SECRET:banana}
2829
deployment:
2930
forged-alliance-exe-path: ${FORGED_ALLIANCE_EXE_PATH}
3031
repositories-directory: ${REPOSITORIES_DIRECTORY:build/cache/repos}
@@ -73,7 +74,8 @@ spring:
7374
oauth2:
7475
resourceserver:
7576
jwt:
76-
issuer-uri: ${JWT_FAF_HYDRA_ISSUER:https://hydra.test.faforever.com/}
77+
jwk-set-uri: http://localhost:4444/.well-known/jwks.json
78+
issuer-uri: http://faf-ory-hydra:4444/
7779
logging:
7880
level:
7981
com.faforever.api: debug

src/main/resources/config/application.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ faf-api:
1414
clan:
1515
invite-link-expire-duration-minutes: ${CLAN_INVITE_LINK_EXPIRE_DURATION_MINUTES:604800}
1616
website-url-format: ${CLAN_WEBSITE_URL_FORMAT:https://clans.${FAF_DOMAIN}/clan/%s}
17+
cloudflare:
18+
hmac-secret: ${CLOUDFLARE_HMAC_SECRET}
19+
hmac-param: ${CLOUDFLARE_HMAC_PARAM:verify}
1720
database:
1821
schema-version: ${DATABASE_SCHEMA_VERSION:126}
1922
deployment:
@@ -25,7 +28,6 @@ faf-api:
2528
forged-alliance-develop-exe-path: ${EXE_UPLOAD_DEVELOP_PATH:/content/legacy-featured-mod-files/updates_fafdevelop_files}
2629
featured-mod:
2730
file-url-format: ${FEATURED_MOD_URL_FORMAT:https://content.${FAF_DOMAIN}/legacy-featured-mod-files/%s/%s}
28-
cloudflare-hmac-secret: ${CLOUDFLARE_HMAC_SECRET}
2931
git-hub:
3032
access-token: ${GITHUB_ACCESS_TOKEN:false}
3133
webhook-secret: ${GITHUB_WEBHOOK_SECRET:false}

0 commit comments

Comments
 (0)