Skip to content

Commit a7ac29c

Browse files
Sheikah45Brutus5000
authored andcommitted
Create inital cloudflare hmac token creation implementation
1 parent 32fc447 commit a7ac29c

5 files changed

Lines changed: 56 additions & 3 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.faforever.api.featuredmods;
2+
3+
import com.faforever.api.AbstractIntegrationTest;
4+
import org.junit.jupiter.api.Test;
5+
import org.springframework.test.context.jdbc.Sql;
6+
7+
import static org.hamcrest.Matchers.hasSize;
8+
import static org.hamcrest.Matchers.is;
9+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
10+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
11+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
12+
13+
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:sql/truncateTables.sql")
14+
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:sql/prepDefaultData.sql")
15+
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:sql/prepFeaturedMods.sql")
16+
public class FeaturedModsControllerTest extends AbstractIntegrationTest {
17+
18+
@Test
19+
public void featuredModFileUrlCorrect() throws Exception {
20+
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")));
25+
}
26+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ public static class Avatar {
137137
@Data
138138
public static class FeaturedMod {
139139
private String fileUrlFormat;
140+
private String cloudflareHmacSecret;
140141
}
141142

142143
@Data

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,24 @@
33
import com.faforever.api.config.FafApiProperties;
44
import org.springframework.stereotype.Component;
55

6+
import javax.crypto.Mac;
7+
import javax.crypto.spec.SecretKeySpec;
68
import javax.inject.Inject;
79
import javax.persistence.PostLoad;
10+
import javax.ws.rs.core.UriBuilder;
11+
import java.net.URI;
12+
import java.net.URLEncoder;
13+
import java.nio.charset.StandardCharsets;
14+
import java.security.InvalidKeyException;
15+
import java.security.NoSuchAlgorithmException;
16+
import java.time.Instant;
17+
import java.util.Base64;
818

919
@Component
1020
public class FeaturedModFileEnricher {
1121

22+
private static final String HMAC_SHA256 = "HmacSHA256";
23+
1224
private static FafApiProperties fafApiProperties;
1325

1426
@Inject
@@ -17,10 +29,22 @@ public void init(FafApiProperties fafApiProperties) {
1729
}
1830

1931
@PostLoad
20-
public void enhance(FeaturedModFile featuredModFile) {
32+
public void enhance(FeaturedModFile featuredModFile) throws NoSuchAlgorithmException, InvalidKeyException {
2133
String folder = featuredModFile.getFolderName();
2234
String urlFormat = fafApiProperties.getFeaturedMod().getFileUrlFormat();
35+
String secret = fafApiProperties.getFeaturedMod().getCloudflareHmacSecret();
36+
long timeStamp = Instant.now().getEpochSecond();
37+
URI featuredModUri = URI.create(String.format(urlFormat, folder, featuredModFile.getOriginalFileName()));
38+
39+
// Builds hmac token for cloudflare firewall verification as specified at
40+
// https://support.cloudflare.com/hc/en-us/articles/115001376488-Configuring-Token-Authentication
41+
Mac mac = Mac.getInstance(HMAC_SHA256);
42+
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_SHA256));
43+
byte[] macMessage = (featuredModUri.getPath() + timeStamp).getBytes(StandardCharsets.UTF_8);
44+
45+
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);
2347

24-
featuredModFile.setUrl(String.format(urlFormat, folder, featuredModFile.getOriginalFileName()));
48+
featuredModFile.setUrl(UriBuilder.fromUri(featuredModUri).queryParam("verify", parameter).build().toString());
2549
}
2650
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ faf-api:
2121
target-directory: ${AVATAR_TARGET_DIRECTORY:build/cache/avatars}
2222
allowed-extensions: ${AVATAR_ALLOWED_FILE_EXTENSIONS:png}
2323
featured-mod:
24-
file-url-format: ${FEATURED_MOD_URL_FORMAT:https://content.test.faforever.com/faf/updaterNew/%s/%s}
24+
file-url-format: ${FEATURED_MOD_URL_FORMAT:https://localhost/legacy-featured-mod-files/%s/%s}
25+
cloudflare-hmac-secret: ${CLOUDFLARE_HMAC_SECRET:banana}
2526
git-hub:
2627
deployment-environment: ${GITHUB_DEPLOYMENT_ENVIRONMENT:development}
2728
deployment:

src/main/resources/config/application.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ faf-api:
2525
forged-alliance-develop-exe-path: ${EXE_UPLOAD_DEVELOP_PATH:/content/legacy-featured-mod-files/updates_fafdevelop_files}
2626
featured-mod:
2727
file-url-format: ${FEATURED_MOD_URL_FORMAT:https://content.${FAF_DOMAIN}/legacy-featured-mod-files/%s/%s}
28+
cloudflare-hmac-secret: ${CLOUDFLARE_HMAC_SECRET}
2829
git-hub:
2930
access-token: ${GITHUB_ACCESS_TOKEN:false}
3031
webhook-secret: ${GITHUB_WEBHOOK_SECRET:false}

0 commit comments

Comments
 (0)