diff --git a/Refresh.Interfaces.Game/Endpoints/Levels/PublishEndpoints.cs b/Refresh.Interfaces.Game/Endpoints/Levels/PublishEndpoints.cs
index 9fbfc276..ab7609ba 100644
--- a/Refresh.Interfaces.Game/Endpoints/Levels/PublishEndpoints.cs
+++ b/Refresh.Interfaces.Game/Endpoints/Levels/PublishEndpoints.cs
@@ -1,3 +1,4 @@
+using System.Net;
using Bunkum.Core;
using Bunkum.Core.Endpoints;
using Bunkum.Core.RateLimit;
@@ -10,6 +11,10 @@
using Refresh.Common.Verification;
using Refresh.Core.Authentication.Permission;
using Refresh.Core.Configuration;
+using Refresh.Core.Helpers;
+using Refresh.Core.Importing;
+using Refresh.Core.Services;
+using Refresh.Core.Types.Assets.Validation;
using Refresh.Core.Types.Data;
using Refresh.Database;
using Refresh.Database.Models;
@@ -36,8 +41,8 @@ public class PublishEndpoints : EndpointGroup
///
/// The level to verify
/// The data context associated with the request
- /// Whether validation succeeded
- private static bool VerifyLevel(GameLevelRequest body, DataContext dataContext)
+ /// Whether validation succeeded. OK = success; everything else = failure
+ private static HttpStatusCode VerifyLevel(GameLevelRequest body, DataContext dataContext, AssetImporter importer, AipiService aipi, bool isActualPublish, bool isInnerLevel = false)
{
if (body.Title.Length > UgcLimits.TitleLimit)
body.Title = body.Title[..UgcLimits.TitleLimit];
@@ -46,24 +51,87 @@ private static bool VerifyLevel(GameLevelRequest body, DataContext dataContext)
body.Description = body.Description[..UgcLimits.DescriptionLimit];
if (body.MaxPlayers is > 4 or < 0 || body.MinPlayers is > 4 or < 0)
- return false;
+ {
+ dataContext.Database.AddPublishFailNotification("Your player number restrictions were invalid.", body.Title, dataContext.User!);
+ return BadRequest;
+ }
+
+ if (body.IsAdventure && isInnerLevel)
+ {
+ dataContext.Database.AddPublishFailNotification("An adventure may not include inner adventures.", body.Title, dataContext.User!);
+ return BadRequest;
+ }
- //If the icon hash is a GUID hash, verify that its a valid texture GUID
- if (body.IconHash.StartsWith('g') && !dataContext.GuidChecker.IsTextureGuid(dataContext.Game, long.Parse(body.IconHash.AsSpan()[1..])))
- return false;
+ if (body.IsAdventure && dataContext.Game != TokenGame.LittleBigPlanet3 && dataContext.Game != TokenGame.BetaBuild)
+ {
+ dataContext.Database.AddPublishFailNotification("You may only publish adventures in LBP3 or beta builds.", body.Title, dataContext.User!);
+ return Unauthorized;
+ }
- if (body.IsAdventure && dataContext.Game != TokenGame.LittleBigPlanet3)
- return false;
+ if (!body.IsAdventure && body.Slots != null && body.Slots.Length > 0)
+ {
+ dataContext.Database.AddPublishFailNotification("Only adventures may include inner levels.", body.Title, dataContext.User!);
+ return BadRequest;
+ }
- GameLevel? existingLevel = dataContext.Database.GetLevelByRootResource(body.RootResource);
- // If all are true:
- // - there is an existing level with this root hash
- // - this isn't an update request
- // then block the upload
- if (existingLevel != null && body.LevelId != existingLevel.LevelId)
+ // Validate icon
+ AssetValidationParameters iconParams = new(body.IconHash, dataContext, importer, aipi)
+ {
+ MustBeTexture = true,
+ MustBeInDataStoreIfHash = isActualPublish, // in most cases no assets will be uploaded yet when startPublish is called
+ AssetContextTypeStr = "icon",
+ OnNewAssetRefCallback = delegate(string newRef) { body.IconHash = newRef; }
+ };
+ ValidatedAssetResult iconResult = ResourceValidationHelper.ValidateReference(iconParams, dataContext.Logger);
+ if (iconResult.Status != OK)
+ {
+ if (iconResult.ErrorMessage != null) dataContext.Database.AddPublishFailNotification(iconResult.ErrorMessage, body.Title, dataContext.User!);
+ return iconResult.Status;
+ }
+
+ // For some stupid reason, inner adventure levels don't include their root hash in the request.
+ // TODO: validate their root hash once we finally start to read them from the adventure root asset itself, as it is included there.
+ if (!isInnerLevel)
{
- dataContext.Database.AddPublishFailNotification("The level you tried to publish has already been uploaded by another user.", body.Title, dataContext.User!);
- return false;
+ // Validate root resource
+ AssetValidationParameters rootParams = new(body.RootResource, dataContext, importer)
+ {
+ MayBeBlank = false,
+ MayBeGuid = false,
+ MustBeInDataStoreIfHash = isActualPublish, // in most cases no assets will be uploaded yet when startPublish is called
+ AssetContextTypeStr = body.IsAdventure ? "adventure asset" : "level asset",
+ OnNewAssetRefCallback = delegate(string newRef) { body.RootResource = newRef; }
+ };
+ ValidatedAssetResult rootResult = ResourceValidationHelper.ValidateReference(rootParams, dataContext.Logger);
+ if (rootResult.Status != OK)
+ {
+ if (rootResult.ErrorMessage != null) dataContext.Database.AddPublishFailNotification(rootResult.ErrorMessage, body.Title, dataContext.User!);
+ return rootResult.Status;
+ }
+ else if (rootResult.AssetInfo != null && dataContext.Game != TokenGame.LittleBigPlanetPSP) // PSP uses a completely different asset type which we don't validate yet
+ {
+ if (body.IsAdventure && rootResult.AssetInfo.AssetType != GameAssetType.AdventureCreateProfile)
+ {
+ if (rootResult.ErrorMessage != null) dataContext.Database.AddPublishFailNotification("The adventure asset was badly formatted.", body.Title, dataContext.User!);
+ return BadRequest;
+ }
+ if (!body.IsAdventure && rootResult.AssetInfo.AssetType != GameAssetType.Level)
+ {
+ if (rootResult.ErrorMessage != null) dataContext.Database.AddPublishFailNotification("The level asset was badly formatted.", body.Title, dataContext.User!);
+ return BadRequest;
+ }
+ }
+
+ GameLevel? existingLevel = dataContext.Database.GetLevelByRootResource(body.RootResource);
+ // If all are true:
+ // - there is an existing level with this root hash
+ // - this isn't an update request
+ // then block the upload
+ if (existingLevel != null && body.LevelId != existingLevel.LevelId)
+ {
+ dataContext.Database.AddPublishFailNotification("The level you tried to publish has already been uploaded by another user.", body.Title, dataContext.User!);
+ return Unauthorized;
+ }
}
if (!string.IsNullOrWhiteSpace(body.PublisherLabels))
@@ -76,7 +144,19 @@ private static bool VerifyLevel(GameLevelRequest body, DataContext dataContext)
.Take(UgcLimits.MaximumLabels);
}
- return true;
+ if (body.Slots != null)
+ {
+ foreach (GameLevelRequest innerLevel in body.Slots)
+ {
+ HttpStatusCode innerValidationResult = VerifyLevel(innerLevel, dataContext, importer, aipi, isActualPublish, true);
+ if (innerValidationResult == OK) continue;
+
+ dataContext.Logger.LogInfo(RefreshContext.Publishing, "Failed to verify inner level {0}", innerLevel.LevelId);
+ return innerValidationResult;
+ }
+ }
+
+ return OK;
}
private static bool IsTimedLevelLimitReached(DataContext dataContext, GameUser user, string levelTitle, EntityUploadRateLimitProperties levelLimit)
@@ -106,6 +186,8 @@ public Response StartPublish(RequestContext context,
GameLevelRequest body,
DataContext dataContext,
GameServerConfig config,
+ AssetImporter importer,
+ AipiService aipi,
GameUser user)
{
if (dataContext.User!.IsWriteBlocked(config))
@@ -117,22 +199,11 @@ public Response StartPublish(RequestContext context,
if (IsTimedLevelLimitReached(dataContext, dataContext.User!, body.Title, user.GetRolePermissionsForUser(config).LevelUploadRateLimit))
return Unauthorized;
- //If verifying the request fails, return BadRequest
- if (!VerifyLevel(body, dataContext))
+ HttpStatusCode validationResult = VerifyLevel(body, dataContext, importer, aipi, false);
+ if (validationResult != OK)
{
- context.Logger.LogInfo(RefreshContext.Publishing, "Failed to verify root level");
- return BadRequest;
- }
-
- if (body.Slots != null)
- {
- foreach (GameLevelRequest innerLevel in body.Slots)
- {
- if (VerifyLevel(innerLevel, dataContext)) continue;
-
- context.Logger.LogInfo(RefreshContext.Publishing, "Failed to verify inner level {0}", innerLevel.LevelId);
- return BadRequest;
- }
+ context.Logger.LogInfo(RefreshContext.Publishing, "Failed to verify level");
+ return validationResult;
}
List hashes =
@@ -163,6 +234,8 @@ public Response PublishLevel(RequestContext context,
GameLevelRequest body,
DataContext dataContext,
GameUser user,
+ AssetImporter importer,
+ AipiService aipi,
GameServerConfig config)
{
if (user.IsWriteBlocked(config))
@@ -172,31 +245,11 @@ public Response PublishLevel(RequestContext context,
if (IsTimedLevelLimitReached(dataContext, user, body.Title, timedLevelLimit))
return Unauthorized;
- //If verifying the request fails, return BadRequest
- if (!VerifyLevel(body, dataContext)) return BadRequest;
-
- string rootResourcePath = context.IsPSP() ? $"psp/{body.RootResource}" : body.RootResource;
-
- //Check if the root resource is a SHA1 hash
- if (!CommonPatterns.Sha1Regex().IsMatch(body.RootResource)) return BadRequest;
- //Make sure the root resource exists in the data store
- if (!dataContext.DataStore.ExistsInStore(rootResourcePath)) return NotFound;
-
- GameAsset? asset = dataContext.Cache.GetAssetInfo(body.RootResource, dataContext.Database);
- if (asset != null && dataContext.Game != TokenGame.LittleBigPlanetPSP)
+ HttpStatusCode validationResult = VerifyLevel(body, dataContext, importer, aipi, true);
+ if (validationResult != OK)
{
- // ReSharper disable once ConvertIfStatementToSwitchStatement
- if (body.IsAdventure && asset.AssetType != GameAssetType.AdventureCreateProfile)
- {
- dataContext.Database.AddPublishFailNotification("The uploaded adventure data was corrupted.", body.Title, dataContext.User!);
- return BadRequest;
- }
-
- if (!body.IsAdventure && asset.AssetType != GameAssetType.Level)
- {
- dataContext.Database.AddPublishFailNotification("The uploaded level data was corrupted.", body.Title, dataContext.User!);
- return BadRequest;
- }
+ context.Logger.LogInfo(RefreshContext.Publishing, "Failed to verify level");
+ return validationResult;
}
if (body.LevelId != 0) // Republish requests contain the id of the old level
diff --git a/RefreshTests.GameServer/Tests/Levels/PublishEndpointsTests.cs b/RefreshTests.GameServer/Tests/Levels/PublishEndpointsTests.cs
index 51a9d3cd..1f2b58d9 100644
--- a/RefreshTests.GameServer/Tests/Levels/PublishEndpointsTests.cs
+++ b/RefreshTests.GameServer/Tests/Levels/PublishEndpointsTests.cs
@@ -15,6 +15,8 @@
using System.Security.Cryptography;
using System.Text;
using System.Net;
+using Refresh.Database.Models.Assets;
+using Bunkum.Core.Storage;
namespace RefreshTests.GameServer.Tests.Levels;
@@ -22,6 +24,14 @@ public class PublishEndpointsTests : GameServerTest
{
private const string TEST_ASSET_HASH = "acddf3f9251c1ddb675ad81ba34ba16135b54aca";
private const string TEST_MISSING_ASSET_HASH = "acddf3f9251c1ddb675ad81ba34ba16135b54acb";
+
+ private string UploadLevelAsset(IDataStore dataStore)
+ {
+ ReadOnlySpan data = "LVLb"u8;
+ string hash = BitConverter.ToString(SHA1.HashData(data)).Replace("-", "").ToLower();
+ dataStore.WriteToStore(hash, data);
+ return hash;
+ }
[Test]
public void PublishLevel()
@@ -210,6 +220,76 @@ public void CantPublishLevelWithInvalidRootResource()
message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
Assert.That(message.StatusCode, Is.EqualTo(BadRequest));
}
+
+ [Test]
+ public void CantPublishLevelWithDisallowedRootResource()
+ {
+ using TestContext context = this.GetServer();
+ GameUser user = context.CreateUser();
+
+ ReadOnlySpan data = "LVLb"u8;
+ string hash = BitConverter.ToString(SHA1.HashData(data)).Replace("-", "").ToLower();
+ context.GetDataStore().WriteToStore(hash, data);
+
+ context.Database.DisallowAsset(hash, GameAssetType.Level, "too many bs obstacles");
+ using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, user);
+
+ GameLevelRequest level = new()
+ {
+ Title = "Boom Town 2",
+ RootResource = hash,
+ };
+
+ HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(Unauthorized));
+
+ message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(Unauthorized));
+ }
+
+ [Test]
+ public void CantPublishLevelWithNoRootResource()
+ {
+ using TestContext context = this.GetServer();
+ GameUser user = context.CreateUser();
+ using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, user);
+
+ GameLevelRequest level = new()
+ {
+ Title = "The best level you've ever played !!!",
+ RootResource = "0",
+ };
+
+ HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(BadRequest));
+
+ message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(BadRequest));
+ }
+
+ [Test]
+ public void CantPublishLevelWithMissingRootResource()
+ {
+ using TestContext context = this.GetServer();
+ GameUser user = context.CreateUser();
+
+ using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, user);
+
+ GameLevelRequest level = new()
+ {
+ Title = "Normal Title!",
+ IconHash = "g719",
+ Description = "Normal Description",
+ Location = new GameLocation(),
+ RootResource = TEST_MISSING_ASSET_HASH,
+ };
+
+ HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(OK));
+
+ message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(NotFound));
+ }
[TestCase(TokenGame.LittleBigPlanet1)]
[TestCase(TokenGame.LittleBigPlanet2)]
@@ -225,10 +305,8 @@ public void CantPublishLevelWithInvalidIconGuid(TokenGame game)
GameLevelRequest level = new()
{
Title = "Normal Title!",
- IconHash = "g0",
- Description = "Normal Description",
- Location = new GameLocation(),
- RootResource = "I AM INVALID!!!",
+ IconHash = "g1087",
+ RootResource = this.UploadLevelAsset(context.GetDataStore()),
};
HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
@@ -238,7 +316,8 @@ public void CantPublishLevelWithInvalidIconGuid(TokenGame game)
Assert.That(message.StatusCode, Is.EqualTo(BadRequest));
}
- public void CanPublishLevelWithInvalidIconGuidPsp()
+ [Test]
+ public void CantPublishLevelWithInvalidIconGuidPsp()
{
using TestContext context = this.GetServer();
GameUser user = context.CreateUser();
@@ -248,41 +327,349 @@ public void CanPublishLevelWithInvalidIconGuidPsp()
GameLevelRequest level = new()
{
Title = "Normal Title!",
- IconHash = "g0",
- Description = "Normal Description",
- Location = new GameLocation(),
- RootResource = "I AM INVALID!!!",
+ IconHash = "g67", // max is g63
+ RootResource = this.UploadLevelAsset(context.GetDataStore()),
};
HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
- Assert.That(message.StatusCode, Is.EqualTo(OK));
+ Assert.That(message.StatusCode, Is.EqualTo(BadRequest));
message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(BadRequest));
+ }
+
+ [Test]
+ public void CantPublishLevelWithMissingCustomIcon()
+ {
+ using TestContext context = this.GetServer();
+ GameUser user = context.CreateUser();
+
+ ReadOnlySpan data = "TEX "u8;
+ string hash = BitConverter.ToString(SHA1.HashData(data)).Replace("-", "").ToLower();
+ // Don't write to store
+
+ using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, user);
+
+ GameLevelRequest level = new()
+ {
+ Title = "67",
+ IconHash = hash,
+ RootResource = this.UploadLevelAsset(context.GetDataStore()),
+ };
+
+ HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
Assert.That(message.StatusCode, Is.EqualTo(OK));
+
+ message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(NotFound));
}
[Test]
- public void CantPublishLevelWithMissingRootResource()
+ public void CantPublishLevelWithDisallowedIcon()
{
using TestContext context = this.GetServer();
GameUser user = context.CreateUser();
+ ReadOnlySpan data = "TEX "u8;
+ string hash = BitConverter.ToString(SHA1.HashData(data)).Replace("-", "").ToLower();
+ context.GetDataStore().WriteToStore(hash, data);
+
+ context.Database.DisallowAsset(hash, GameAssetType.Texture, "too cringe");
using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, user);
GameLevelRequest level = new()
{
- Title = "Normal Title!",
- IconHash = "g719",
- Description = "Normal Description",
- Location = new GameLocation(),
- RootResource = TEST_MISSING_ASSET_HASH,
+ Title = "67",
+ IconHash = hash,
+ RootResource = this.UploadLevelAsset(context.GetDataStore()),
+ };
+
+ HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(Unauthorized));
+
+ message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(Unauthorized));
+ }
+
+ [Test]
+ [TestCase(TokenGame.LittleBigPlanet1, false)]
+ [TestCase(TokenGame.LittleBigPlanetPSP, false)]
+ [TestCase(TokenGame.LittleBigPlanet3, true)]
+ [TestCase(TokenGame.BetaBuild, true)]
+ public void TestAdventureUploadsFromVariousGames(TokenGame game, bool success)
+ {
+ using TestContext context = this.GetServer();
+ GameUser user = context.CreateUser();
+
+ ReadOnlySpan data = "ADCb"u8;
+ string hash = BitConverter.ToString(SHA1.HashData(data)).Replace("-", "").ToLower();
+ context.GetDataStore().WriteToStore(hash, data);
+
+ using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, game, TokenPlatform.PS3, user);
+
+ GameLevelRequest level = new()
+ {
+ Title = "adventure time!",
+ IsAdventure = true,
+ Description = "epic joke please laugh",
+ RootResource = hash,
+ };
+
+ HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(success ? OK : Unauthorized));
+
+ message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(success ? OK : Unauthorized));
+ }
+
+ [Test]
+ [TestCase("LVLb", false)]
+ [TestCase("PLNb", false)]
+ [TestCase("CHKb", false)]
+ [TestCase("TEX ", false)]
+ [TestCase("ADCb", true)]
+ public void TestAdventureRootResourceTypes(string resource, bool success)
+ {
+ using TestContext context = this.GetServer();
+ GameUser user = context.CreateUser();
+
+ ReadOnlySpan data = new(Encoding.UTF8.GetBytes(resource));
+ string hash = BitConverter.ToString(SHA1.HashData(data)).Replace("-", "").ToLower();
+ context.GetDataStore().WriteToStore(hash, data);
+
+ using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, TokenGame.LittleBigPlanet3, TokenPlatform.PS3, user);
+
+ GameLevelRequest level = new()
+ {
+ Title = "totally an adventure!",
+ IsAdventure = true,
+ RootResource = hash,
+ };
+
+ HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(success ? OK : BadRequest));
+
+ message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(success ? OK : BadRequest));
+ }
+
+ [Test]
+ [TestCase("LVLb", true)]
+ [TestCase("PLNb", false)]
+ [TestCase("CHKb", false)]
+ [TestCase("TEX ", false)]
+ [TestCase("ADCb", false)]
+ public void TestLevelRootResourceTypes(string resource, bool success)
+ {
+ using TestContext context = this.GetServer();
+ GameUser user = context.CreateUser();
+
+ ReadOnlySpan data = new(Encoding.UTF8.GetBytes(resource));
+ string hash = BitConverter.ToString(SHA1.HashData(data)).Replace("-", "").ToLower();
+ context.GetDataStore().WriteToStore(hash, data);
+
+ using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, TokenGame.LittleBigPlanet3, TokenPlatform.PS3, user);
+
+ GameLevelRequest level = new()
+ {
+ Title = "totally a level!",
+ IsAdventure = false,
+ RootResource = hash,
+ };
+
+ HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(success ? OK : BadRequest));
+
+ message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(success ? OK : BadRequest));
+ }
+
+ [Test]
+ [TestCase("LVLb")]
+ [TestCase("PLNb")]
+ [TestCase("CHKb")]
+ [TestCase("TEX ")]
+ [TestCase("wasedrthzjtgerw")]
+ public void LevelRootResourceTypeIsIgnoredIfPSP(string resource)
+ {
+ using TestContext context = this.GetServer();
+ GameUser user = context.CreateUser();
+
+ ReadOnlySpan data = new(Encoding.UTF8.GetBytes(resource));
+ string hash = BitConverter.ToString(SHA1.HashData(data)).Replace("-", "").ToLower();
+ context.GetDataStore().WriteToStore($"psp/{hash}", data);
+
+ using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, TokenGame.LittleBigPlanetPSP, TokenPlatform.PSP, user);
+ client.DefaultRequestHeaders.UserAgent.TryParseAdd("LBPPSP CLIENT");
+
+ GameLevelRequest level = new()
+ {
+ Title = "totally a level!",
+ IsAdventure = false,
+ RootResource = hash,
};
HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
Assert.That(message.StatusCode, Is.EqualTo(OK));
message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
- Assert.That(message.StatusCode, Is.EqualTo(NotFound));
+ Assert.That(message.StatusCode, Is.EqualTo(OK));
+ }
+
+ [Test]
+ public void CantPublishRegularLevelWithInnerLevels()
+ {
+ using TestContext context = this.GetServer();
+ GameUser user = context.CreateUser();
+
+ ReadOnlySpan data = "LVLb"u8;
+ string hash = BitConverter.ToString(SHA1.HashData(data)).Replace("-", "").ToLower();
+ context.GetDataStore().WriteToStore(hash, data);
+
+ using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, TokenGame.LittleBigPlanet3, TokenPlatform.PS3, user);
+
+ GameLevelRequest level = new()
+ {
+ Title = "totally an adventure!",
+ IsAdventure = false,
+ RootResource = hash,
+ Slots =
+ [
+ new()
+ {
+ Title = "Hi lol",
+ RootResource = "",
+ }
+ ]
+ };
+
+ HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(BadRequest));
+
+ message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(BadRequest));
+ }
+
+ [Test]
+ public void CanPublishAdventureWithInnerLevels()
+ {
+ using TestContext context = this.GetServer();
+ GameUser user = context.CreateUser();
+
+ ReadOnlySpan data = "ADCb"u8;
+ string hash = BitConverter.ToString(SHA1.HashData(data)).Replace("-", "").ToLower();
+ context.GetDataStore().WriteToStore(hash, data);
+
+ using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, TokenGame.LittleBigPlanet3, TokenPlatform.PS3, user);
+
+ GameLevelRequest level = new()
+ {
+ Title = "totally an adventure!",
+ IsAdventure = true,
+ RootResource = hash,
+ Slots =
+ [
+ new()
+ {
+ Title = "Hi lol",
+ RootResource = "",
+ }
+ ]
+ };
+
+ HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(OK));
+
+ message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(OK));
+ }
+
+ [Test]
+ public void CantPublishAdventureWithRecursiveInnerLevels()
+ {
+ using TestContext context = this.GetServer();
+ GameUser user = context.CreateUser();
+
+ ReadOnlySpan data = "ADCb"u8;
+ string hash = BitConverter.ToString(SHA1.HashData(data)).Replace("-", "").ToLower();
+ context.GetDataStore().WriteToStore(hash, data);
+
+ using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, TokenGame.LittleBigPlanet3, TokenPlatform.PS3, user);
+
+ GameLevelRequest level = new()
+ {
+ Title = "Deep adventure",
+ IsAdventure = true,
+ RootResource = hash,
+ Slots =
+ [
+ new()
+ {
+ Title = "Hi lol",
+ IsAdventure = false,
+ RootResource = "",
+ Slots =
+ [
+ new()
+ {
+ Title = "Super secret",
+ IsAdventure = false,
+ RootResource = "",
+ Slots =
+ [
+ new()
+ {
+ Title = "This is getting ridiculous...",
+ IsAdventure = false,
+ RootResource = "",
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ };
+
+ HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(BadRequest));
+
+ message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(BadRequest));
+ }
+
+ [Test]
+ public void CantPublishAdventureWithInnerAdventures()
+ {
+ using TestContext context = this.GetServer();
+ GameUser user = context.CreateUser();
+
+ ReadOnlySpan data = "ADCb"u8;
+ string hash = BitConverter.ToString(SHA1.HashData(data)).Replace("-", "").ToLower();
+ context.GetDataStore().WriteToStore(hash, data);
+
+ using HttpClient client = context.GetAuthenticatedClient(TokenType.Game, TokenGame.LittleBigPlanet3, TokenPlatform.PS3, user);
+
+ GameLevelRequest level = new()
+ {
+ Title = "Very adventurous",
+ IsAdventure = true,
+ RootResource = hash,
+ Slots =
+ [
+ new()
+ {
+ Title = "Hi lol",
+ IsAdventure = true,
+ RootResource = "",
+ }
+ ]
+ };
+
+ HttpResponseMessage message = client.PostAsync("/lbp/startPublish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(BadRequest));
+
+ message = client.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
+ Assert.That(message.StatusCode, Is.EqualTo(BadRequest));
}
[Test]
@@ -448,7 +835,7 @@ public void CantPublishSameRootLevelHashTwice()
//As user 2, try to publish a level with the same root hash
message = client2.PostAsync("/lbp/publish", new StringContent(level.AsXML())).Result;
- Assert.That(message.StatusCode, Is.EqualTo(BadRequest));
+ Assert.That(message.StatusCode, Is.EqualTo(Unauthorized));
}
[Test]