-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
1,409 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package com.faforever.api.deployment; | ||
|
||
import com.faforever.api.AbstractIntegrationTest; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
import org.springframework.mock.web.MockMultipartFile; | ||
import org.springframework.test.context.jdbc.Sql; | ||
|
||
import java.nio.file.Files; | ||
import java.nio.file.Paths; | ||
|
||
import static org.junit.Assert.assertTrue; | ||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
|
||
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:sql/prepFeaturedMods.sql") | ||
@Sql(executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, scripts = "classpath:sql/cleanFeaturedMods.sql") | ||
public class ExeUploadControllerTest extends AbstractIntegrationTest { | ||
private MockMultipartFile file; | ||
private static final String SUPER_SECRET = "banana"; | ||
|
||
@Before | ||
public void setUp() { | ||
super.setUp(); | ||
file = new MockMultipartFile("file", "ForgedAlliance.exe", "application/octet-stream", new byte[]{ 1, 2 ,3, 4 }); | ||
} | ||
|
||
@Test | ||
public void testSuccessUploadBeta() throws Exception { | ||
this.mockMvc.perform(fileUpload("/exe/upload") | ||
.file(file) | ||
.param("modName", "fafbeta") | ||
.param("apiKey", SUPER_SECRET) | ||
).andExpect(status().isOk()); | ||
assertTrue(Files.exists(Paths.get("build/exe/beta/ForgedAlliance.3706.exe"))); | ||
} | ||
|
||
@Test | ||
public void testSuccessUploadDevelop() throws Exception { | ||
this.mockMvc.perform(fileUpload("/exe/upload") | ||
.file(file) | ||
.param("modName", "fafdevelop") | ||
.param("apiKey", SUPER_SECRET) | ||
).andExpect(status().isOk()); | ||
assertTrue(Files.exists(Paths.get("build/exe/develop/ForgedAlliance.3707.exe"))); | ||
} | ||
|
||
@Test | ||
public void testBadRequestUploadNoModName() throws Exception { | ||
this.mockMvc.perform(fileUpload("/exe/upload") | ||
.file(file) | ||
.param("apiKey", SUPER_SECRET) | ||
).andExpect(status().is4xxClientError()); | ||
} | ||
|
||
@Test | ||
public void testBadRequestUploadNoFile() throws Exception { | ||
this.mockMvc.perform(fileUpload("/exe/upload") | ||
.param("modName", "fafdevelop") | ||
.param("apiKey", SUPER_SECRET) | ||
).andExpect(status().is4xxClientError()); | ||
} | ||
|
||
@Test | ||
public void testBadRequestUploadFileWithWrongExeExtension() throws Exception { | ||
MockMultipartFile file = new MockMultipartFile("file", "ForgedAlliance.zip", "application/octet-stream", new byte[]{ 1, 2 ,3, 4 }); | ||
this.mockMvc.perform(fileUpload("/exe/upload") | ||
.file(file) | ||
.param("modName", "fafbeta") | ||
.param("apiKey", SUPER_SECRET) | ||
).andExpect(status().is4xxClientError()); | ||
} | ||
|
||
@Test | ||
public void testBadRequestUploadWithoutApiKey() throws Exception { | ||
this.mockMvc.perform(fileUpload("/exe/upload") | ||
.file(file) | ||
.param("modName", "fafbeta") | ||
).andExpect(status().is4xxClientError()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
DROP TABLE updates_fafdevelop_files; | ||
DELETE FROM updates_fafbeta_files; | ||
DROP TABLE updates_fafdevelop; | ||
DELETE FROM updates_fafbeta; | ||
DELETE FROM game_featuredMods; |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
src/main/java/com/faforever/api/deployment/ExeUploaderController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package com.faforever.api.deployment; | ||
|
||
import com.faforever.api.config.FafApiProperties; | ||
import com.faforever.api.error.ApiException; | ||
import com.faforever.api.error.Error; | ||
import com.faforever.api.error.ErrorCode; | ||
import com.google.common.io.Files; | ||
import io.swagger.annotations.ApiOperation; | ||
import io.swagger.annotations.ApiResponse; | ||
import io.swagger.annotations.ApiResponses; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestMethod; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE; | ||
|
||
@RestController | ||
@RequestMapping(path = "/exe") | ||
@Slf4j | ||
public class ExeUploaderController { | ||
private final FafApiProperties apiProperties; | ||
private final ExeUploaderService exeUploaderService; | ||
|
||
public ExeUploaderController( | ||
FafApiProperties apiProperties, | ||
ExeUploaderService exeUploaderService | ||
) { | ||
this.apiProperties = apiProperties; | ||
this.exeUploaderService = exeUploaderService; | ||
} | ||
|
||
@ApiOperation("Upload an exe file") | ||
@ApiResponses(value = { | ||
@ApiResponse(code = 200, message = "Success"), | ||
@ApiResponse(code = 401, message = "Unauthorized"), | ||
@ApiResponse(code = 500, message = "Failure")}) | ||
@RequestMapping(path = "/upload", method = RequestMethod.POST, produces = APPLICATION_JSON_UTF8_VALUE) | ||
public void upload(@RequestParam("file") MultipartFile file, | ||
@RequestParam("modName") String modName, | ||
@RequestParam("apiKey") String apiKey | ||
) throws Exception { | ||
if (!apiKey.equals(apiProperties.getDeployment().getTestingExeUploadKey())) { | ||
throw new ApiException(new Error(ErrorCode.API_KEY_INVALID)); | ||
} | ||
String extension = Files.getFileExtension(file.getOriginalFilename()); | ||
if (!apiProperties.getDeployment().getAllowedExeExtension().equals(extension)) { | ||
throw new ApiException( | ||
new Error(ErrorCode.UPLOAD_INVALID_FILE_EXTENSIONS, apiProperties.getDeployment().getAllowedExeExtension()) | ||
); | ||
} | ||
|
||
log.info("Uploading exe file '{}' to '{}' directory", file.getOriginalFilename(), modName); | ||
|
||
exeUploaderService.processUpload(file.getBytes(), modName); | ||
} | ||
} |
95 changes: 95 additions & 0 deletions
95
src/main/java/com/faforever/api/deployment/ExeUploaderService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package com.faforever.api.deployment; | ||
|
||
import com.faforever.api.config.FafApiProperties; | ||
import com.faforever.api.content.ContentService; | ||
import com.faforever.api.error.ApiException; | ||
import com.faforever.api.error.Error; | ||
import com.faforever.api.error.ErrorCode; | ||
import com.faforever.api.featuredmods.FeaturedModFile; | ||
import com.faforever.api.featuredmods.FeaturedModService; | ||
import com.faforever.api.utils.FilePermissionUtil; | ||
import lombok.SneakyThrows; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import org.springframework.util.Assert; | ||
|
||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.nio.file.StandardCopyOption; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
import static com.google.common.hash.Hashing.md5; | ||
import static com.google.common.io.Files.hash; | ||
import static java.nio.file.Files.createDirectories; | ||
|
||
/** | ||
* remove this shit | ||
*/ | ||
@Service | ||
@Slf4j | ||
public class ExeUploaderService { | ||
private final ContentService contentService; | ||
private final FafApiProperties apiProperties; | ||
private final FeaturedModService featuredModService; | ||
|
||
public ExeUploaderService( | ||
ContentService contentService, | ||
FafApiProperties apiProperties, | ||
FeaturedModService featuredModService | ||
) { | ||
this.contentService = contentService; | ||
this.apiProperties = apiProperties; | ||
this.featuredModService = featuredModService; | ||
} | ||
|
||
@Transactional | ||
@SneakyThrows | ||
public void processUpload(byte[] bytes, String modName) { | ||
checkAllowedBranchName(modName); | ||
FeaturedModFile featuredModFile = featuredModService.getFile(modName, null, "ForgedAlliance.exe"); | ||
featuredModFile.setName(String.format("ForgedAlliance.%d.exe", featuredModFile.getVersion())); | ||
Path uploadedFile = this.upload( | ||
bytes, | ||
featuredModFile.getName(), | ||
modName | ||
); | ||
featuredModFile.setMd5(hash(uploadedFile.toFile(), md5()).toString()); | ||
|
||
List<FeaturedModFile> featuredModFiles = Collections.singletonList(featuredModFile); | ||
featuredModService.save(modName, (short) featuredModFile.getVersion(), featuredModFiles); | ||
} | ||
|
||
@SneakyThrows | ||
public Path upload(byte[] exeData, String fileName, String modName) { | ||
Assert.isTrue(exeData.length > 0, "data of 'ForgedAlliance.exe' must not be empty"); | ||
|
||
Path tempDir = contentService.createTempDir(); | ||
Path temporaryFile = tempDir.resolve(fileName); | ||
Files.write(temporaryFile, exeData); | ||
|
||
Path copyTo = Paths.get(getCopyToPath(modName, fileName)); | ||
createDirectories(copyTo.getParent(), FilePermissionUtil.directoryPermissionFileAttributes()); | ||
return Files.copy( | ||
temporaryFile, | ||
copyTo, | ||
StandardCopyOption.REPLACE_EXISTING | ||
); | ||
} | ||
|
||
private void checkAllowedBranchName(String modName) throws ApiException { | ||
if (!"fafbeta".equals(modName) && !"fafdevelop".equals(modName)) { | ||
throw new ApiException(new Error(ErrorCode.INVALID_FEATURED_MOD, modName)); | ||
} | ||
} | ||
|
||
private String getCopyToPath(String modName, String fileName) { | ||
String copyTo = apiProperties.getDeployment().getForgedAllianceBetaExePath(); | ||
if ("fafdevelop".equals(modName)) { | ||
copyTo = apiProperties.getDeployment().getForgedAllianceDevelopExePath(); | ||
} | ||
return copyTo + "/" + fileName; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.