mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-05-13 13:22:28 +00:00
Refactor deserialization and authentication (#550)
* Refactor deserialization and more * Refactor authentication flow * Fix unit tests * Make deserialization better
This commit is contained in:
parent
505b5eb03b
commit
b3a00da554
48 changed files with 575 additions and 589 deletions
|
@ -1,14 +1,17 @@
|
|||
#nullable enable
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/plain")]
|
||||
public class ClientConfigurationController : ControllerBase
|
||||
|
@ -22,11 +25,8 @@ public class ClientConfigurationController : ControllerBase
|
|||
|
||||
[HttpGet("network_settings.nws")]
|
||||
[SuppressMessage("ReSharper", "StringLiteralTypo")]
|
||||
public async Task<IActionResult> NetworkSettings()
|
||||
public IActionResult NetworkSettings()
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
string hostname = ServerConfiguration.Instance.GameApiExternalUrl;
|
||||
return this.Ok
|
||||
(
|
||||
|
@ -52,7 +52,9 @@ public class ClientConfigurationController : ControllerBase
|
|||
[Produces("text/xml")]
|
||||
public async Task<IActionResult> GetPrivacySettings()
|
||||
{
|
||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
PrivacySettings ps = new()
|
||||
|
@ -71,11 +73,7 @@ public class ClientConfigurationController : ControllerBase
|
|||
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
this.Request.Body.Position = 0;
|
||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
||||
XmlSerializer serializer = new(typeof(PrivacySettings));
|
||||
PrivacySettings? settings = (PrivacySettings?)serializer.Deserialize(new StringReader(bodyString));
|
||||
PrivacySettings? settings = await this.DeserializeBody<PrivacySettings>();
|
||||
if (settings == null) return this.BadRequest();
|
||||
|
||||
if (settings.LevelVisibility != null)
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
#nullable enable
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Levels;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/xml")]
|
||||
public class CommentController : ControllerBase
|
||||
|
@ -26,10 +28,10 @@ public class CommentController : ControllerBase
|
|||
[HttpPost("rateComment/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> RateComment([FromQuery] int commentId, [FromQuery] int rating, string? username, string? slotType, int slotId)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (username == null && (SlotHelper.IsTypeInvalid(slotType) || slotId == 0)) return this.BadRequest();
|
||||
// Return bad request if both are true or both are false
|
||||
if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest();
|
||||
|
||||
bool success = await this.database.RateComment(token.UserId, commentId, rating);
|
||||
if (!success) return this.BadRequest();
|
||||
|
@ -41,37 +43,33 @@ public class CommentController : ControllerBase
|
|||
[HttpGet("userComments/{username}")]
|
||||
public async Task<IActionResult> GetComments([FromQuery] int pageStart, [FromQuery] int pageSize, string? username, string? slotType, int slotId)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
if (pageSize <= 0 || pageStart < 0) return this.BadRequest();
|
||||
|
||||
int targetId = slotId;
|
||||
CommentType type = CommentType.Level;
|
||||
if (!string.IsNullOrWhiteSpace(username))
|
||||
if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest();
|
||||
|
||||
if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
|
||||
|
||||
int targetId;
|
||||
CommentType type = username == null ? CommentType.Level : CommentType.Profile;
|
||||
|
||||
if (type == CommentType.Level)
|
||||
{
|
||||
targetId = this.database.Users.First(u => u.Username.Equals(username)).UserId;
|
||||
type = CommentType.Profile;
|
||||
targetId = await this.database.Slots.Where(s => s.SlotId == slotId)
|
||||
.Where(s => s.CommentsEnabled && !s.Hidden)
|
||||
.Select(s => s.SlotId)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SlotHelper.IsTypeInvalid(slotType) || slotId == 0) return this.BadRequest();
|
||||
targetId = await this.database.Users.Where(u => u.Username == username)
|
||||
.Where(u => u.CommentsEnabled)
|
||||
.Select(u => u.UserId)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
if (type == CommentType.Level && slotType == "developer") targetId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
|
||||
|
||||
if (type == CommentType.Profile)
|
||||
{
|
||||
User? profile = await this.database.Users.FirstOrDefaultAsync(s => s.UserId == targetId);
|
||||
if (profile == null) return this.BadRequest();
|
||||
if (!profile.CommentsEnabled) return this.NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == targetId);
|
||||
if (slot == null) return this.BadRequest();
|
||||
if (!slot.CommentsEnabled) return this.NotFound();
|
||||
}
|
||||
if (targetId == 0) return this.NotFound();
|
||||
|
||||
List<Comment> comments = await this.database.Comments.Include
|
||||
(c => c.Poster)
|
||||
|
@ -88,7 +86,8 @@ public class CommentController : ControllerBase
|
|||
|
||||
private async Task<int> getReaction(int userId, int commentId)
|
||||
{
|
||||
return await this.database.Reactions.Where(r => r.UserId == userId && r.TargetId == commentId)
|
||||
return await this.database.Reactions.Where(r => r.UserId == userId)
|
||||
.Where(r => r.TargetId == commentId)
|
||||
.Select(r => r.Rating)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
@ -97,28 +96,28 @@ public class CommentController : ControllerBase
|
|||
[HttpPost("postComment/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> PostComment(string? username, string? slotType, int slotId)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
this.Request.Body.Position = 0;
|
||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
||||
XmlSerializer serializer = new(typeof(Comment));
|
||||
Comment? comment = (Comment?)serializer.Deserialize(new StringReader(bodyString));
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(comment);
|
||||
|
||||
CommentType type = (slotId == 0 ? CommentType.Profile : CommentType.Level);
|
||||
|
||||
if (type == CommentType.Level && (SlotHelper.IsTypeInvalid(slotType) || slotId == 0)) return this.BadRequest();
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
Comment? comment = await this.DeserializeBody<Comment>();
|
||||
if (comment == null) return this.BadRequest();
|
||||
|
||||
int targetId = slotId;
|
||||
if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest();
|
||||
|
||||
if (type == CommentType.Profile) targetId = this.database.Users.First(u => u.Username == username).UserId;
|
||||
CommentType type = username == null ? CommentType.Level : CommentType.Profile;
|
||||
|
||||
if (slotType == "developer") targetId = await SlotHelper.GetPlaceholderSlotId(this.database, targetId, SlotType.Developer);
|
||||
int targetId;
|
||||
if (type == CommentType.Level)
|
||||
{
|
||||
slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
|
||||
targetId = await this.database.Slots.Where(s => s.SlotId == slotId)
|
||||
.Where(s => s.CommentsEnabled && !s.Hidden)
|
||||
.Select(s => s.SlotId)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
targetId = await this.database.UserIdFromUsername(username!);
|
||||
}
|
||||
|
||||
bool success = await this.database.PostComment(token.UserId, targetId, type, comment.Message);
|
||||
if (success) return this.Ok();
|
||||
|
@ -130,44 +129,44 @@ public class CommentController : ControllerBase
|
|||
[HttpPost("deleteComment/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> DeleteComment([FromQuery] int commentId, string? username, string? slotType, int slotId)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest();
|
||||
|
||||
Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId);
|
||||
if (comment == null) return this.NotFound();
|
||||
|
||||
if (comment.Type == CommentType.Level && (SlotHelper.IsTypeInvalid(slotType) || slotId == 0)) return this.BadRequest();
|
||||
if (comment.Deleted) return this.Ok();
|
||||
|
||||
if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
|
||||
|
||||
// if you are not the poster
|
||||
if (comment.PosterUserId != token.UserId)
|
||||
bool canDelete;
|
||||
if (comment.Type == CommentType.Profile)
|
||||
{
|
||||
if (comment.Type == CommentType.Profile)
|
||||
{
|
||||
// if you aren't the poster and aren't the profile owner
|
||||
if (comment.TargetId != token.UserId)
|
||||
{
|
||||
return this.StatusCode(403, "");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == comment.TargetId);
|
||||
// if you aren't the creator of the level
|
||||
if (slot == null || slot.CreatorId != token.UserId || slotId != slot.SlotId)
|
||||
{
|
||||
return this.StatusCode(403, "");
|
||||
}
|
||||
}
|
||||
canDelete = comment.PosterUserId == token.UserId || comment.TargetId == token.UserId;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
|
||||
|
||||
if (slotId != comment.TargetId) return this.BadRequest();
|
||||
|
||||
int slotCreator = await this.database.Slots.Where(s => s.SlotId == comment.TargetId)
|
||||
.Where(s => s.CommentsEnabled)
|
||||
.Select(s => s.CreatorId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
// Comments are disabled or the slot doesn't have a creator
|
||||
if (slotCreator == 0) return this.BadRequest();
|
||||
|
||||
canDelete = comment.PosterUserId == token.UserId || slotCreator == token.UserId;
|
||||
}
|
||||
|
||||
if (!canDelete) return this.StatusCode(403, "");
|
||||
|
||||
comment.Deleted = true;
|
||||
comment.DeletedBy = await this.database.UsernameFromGameToken(token);
|
||||
comment.DeletedType = "user";
|
||||
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
return this.Ok();
|
||||
}
|
||||
}
|
|
@ -1,28 +1,14 @@
|
|||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/xml")]
|
||||
public class DeveloperController : Controller
|
||||
{
|
||||
|
||||
private readonly Database database;
|
||||
|
||||
public DeveloperController(Database database)
|
||||
{
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
[HttpGet("developer_videos")]
|
||||
public async Task<IActionResult> DeveloperVideos()
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
return this.Ok("<videos></videos>");
|
||||
}
|
||||
public IActionResult DeveloperVideos() => this.Ok("<videos></videos>");
|
||||
}
|
|
@ -1,16 +1,18 @@
|
|||
#nullable enable
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.StorableLists.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
public class FriendsController : ControllerBase
|
||||
{
|
||||
|
@ -24,14 +26,9 @@ public class FriendsController : ControllerBase
|
|||
[HttpPost("npdata")]
|
||||
public async Task<IActionResult> NPData()
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
this.Request.Body.Position = 0;
|
||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
||||
XmlSerializer serializer = new(typeof(NPData));
|
||||
NPData? npData = (NPData?)serializer.Deserialize(new StringReader(bodyString));
|
||||
NPData? npData = await this.DeserializeBody<NPData>();
|
||||
if (npData == null) return this.BadRequest();
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(npData);
|
||||
|
@ -73,7 +70,6 @@ public class FriendsController : ControllerBase
|
|||
|
||||
if (userAndToken == null) return this.StatusCode(403, "");
|
||||
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
User user = userAndToken.Value.Item1;
|
||||
GameToken gameToken = userAndToken.Value.Item2;
|
||||
|
||||
|
|
|
@ -72,7 +72,14 @@ public class LoginController : ControllerBase
|
|||
}
|
||||
}
|
||||
|
||||
User? user = await this.database.UserFromGameToken(token, true);
|
||||
// The GameToken LINQ statement above is case insensitive so we check that they are equal here
|
||||
if (token.User.Username != npTicket.Username)
|
||||
{
|
||||
Logger.Warn($"Username case does not match for user {npTicket.Username}, expected={token.User.Username}", LogArea.Login);
|
||||
return this.StatusCode(403, "");
|
||||
}
|
||||
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
|
||||
if (user == null || user.IsBanned)
|
||||
{
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/goodbye")]
|
||||
[Produces("text/xml")]
|
||||
public class LogoutController : ControllerBase
|
||||
|
@ -23,10 +25,9 @@ public class LogoutController : ControllerBase
|
|||
[HttpPost]
|
||||
public async Task<IActionResult> OnPost()
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
User? user = await this.database.Users.Where(u => u.UserId == token.UserId).FirstOrDefaultAsync();
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
user.LastLogout = TimeHelper.TimestampMillis;
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Levels;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Matching;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
// [Produces("text/plain")]
|
||||
[Produces("text/xml")]
|
||||
public class EnterLevelController : ControllerBase
|
||||
{
|
||||
private readonly Database database;
|
||||
|
@ -22,8 +25,7 @@ public class EnterLevelController : ControllerBase
|
|||
[HttpPost("play/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> PlayLevel(string slotType, int slotId)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();
|
||||
|
||||
|
@ -33,13 +35,11 @@ public class EnterLevelController : ControllerBase
|
|||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
if (slot == null) return this.StatusCode(403, "");
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
IQueryable<VisitedLevel> visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == token.UserId);
|
||||
VisitedLevel? v;
|
||||
if (!visited.Any())
|
||||
{
|
||||
switch (gameVersion)
|
||||
switch (token.GameVersion)
|
||||
{
|
||||
case GameVersion.LittleBigPlanet2:
|
||||
case GameVersion.LittleBigPlanetVita:
|
||||
|
@ -48,6 +48,9 @@ public class EnterLevelController : ControllerBase
|
|||
case GameVersion.LittleBigPlanet3:
|
||||
slot.PlaysLBP3Unique++;
|
||||
break;
|
||||
case GameVersion.LittleBigPlanet1:
|
||||
case GameVersion.LittleBigPlanetPSP:
|
||||
case GameVersion.Unknown:
|
||||
default: return this.BadRequest();
|
||||
}
|
||||
|
||||
|
@ -65,7 +68,7 @@ public class EnterLevelController : ControllerBase
|
|||
|
||||
if (v == null) return this.NotFound();
|
||||
|
||||
switch (gameVersion)
|
||||
switch (token.GameVersion)
|
||||
{
|
||||
case GameVersion.LittleBigPlanet2:
|
||||
case GameVersion.LittleBigPlanetVita:
|
||||
|
@ -76,9 +79,9 @@ public class EnterLevelController : ControllerBase
|
|||
slot.PlaysLBP3++;
|
||||
v.PlaysLBP3++;
|
||||
break;
|
||||
case GameVersion.LittleBigPlanetPSP: throw new NotImplementedException();
|
||||
case GameVersion.Unknown:
|
||||
case GameVersion.LittleBigPlanet1:
|
||||
case GameVersion.LittleBigPlanetPSP:
|
||||
case GameVersion.Unknown:
|
||||
default:
|
||||
return this.BadRequest();
|
||||
}
|
||||
|
@ -92,8 +95,7 @@ public class EnterLevelController : ControllerBase
|
|||
[HttpPost("enterLevel/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> EnterLevel(string slotType, int slotId)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();
|
||||
|
||||
|
|
|
@ -8,12 +8,14 @@ using LBPUnion.ProjectLighthouse.Match.MatchCommands;
|
|||
using LBPUnion.ProjectLighthouse.Match.Rooms;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Matching;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/xml")]
|
||||
public class MatchController : ControllerBase
|
||||
|
@ -27,26 +29,16 @@ public class MatchController : ControllerBase
|
|||
|
||||
[HttpPost("gameState")]
|
||||
[Produces("text/plain")]
|
||||
public async Task<IActionResult> GameState()
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
return this.Ok("VALID");
|
||||
}
|
||||
public IActionResult GameState() => this.Ok("VALID");
|
||||
|
||||
[HttpPost("match")]
|
||||
[Produces("text/plain")]
|
||||
public async Task<IActionResult> Match()
|
||||
{
|
||||
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (userAndToken == null) return this.StatusCode(403, "");
|
||||
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
User user = userAndToken.Value.Item1;
|
||||
GameToken gameToken = userAndToken.Value.Item2;
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
#region Parse match data
|
||||
|
||||
|
@ -81,70 +73,74 @@ public class MatchController : ControllerBase
|
|||
|
||||
#endregion
|
||||
|
||||
await LastContactHelper.SetLastContact(this.database, user, gameToken.GameVersion, gameToken.Platform);
|
||||
await LastContactHelper.SetLastContact(this.database, user, token.GameVersion, token.Platform);
|
||||
|
||||
#region Process match data
|
||||
|
||||
if (matchData is UpdateMyPlayerData playerData)
|
||||
switch (matchData)
|
||||
{
|
||||
MatchHelper.SetUserLocation(user.UserId, gameToken.UserLocation);
|
||||
Room? room = RoomHelper.FindRoomByUser(user.UserId, gameToken.GameVersion, gameToken.Platform, true);
|
||||
|
||||
if (playerData.RoomState != null)
|
||||
if (room != null && Equals(room.HostId, user.UserId))
|
||||
room.State = (RoomState)playerData.RoomState;
|
||||
}
|
||||
|
||||
// Check how many people are online in release builds, disabled for debug for ..well debugging.
|
||||
#if DEBUG
|
||||
else if (matchData is FindBestRoom diveInData)
|
||||
#else
|
||||
else if (matchData is FindBestRoom diveInData && MatchHelper.UserLocations.Count > 1)
|
||||
#endif
|
||||
{
|
||||
FindBestRoomResponse? response = RoomHelper.FindBestRoom
|
||||
(user, gameToken.GameVersion, diveInData.RoomSlot, gameToken.Platform, gameToken.UserLocation);
|
||||
|
||||
if (response == null) return this.NotFound();
|
||||
|
||||
string serialized = JsonSerializer.Serialize(response, typeof(FindBestRoomResponse));
|
||||
foreach (Player player in response.Players) MatchHelper.AddUserRecentlyDivedIn(user.UserId, player.User.UserId);
|
||||
|
||||
return this.Ok($"[{{\"StatusCode\":200}},{serialized}]");
|
||||
}
|
||||
|
||||
else if (matchData is CreateRoom createRoom && MatchHelper.UserLocations.Count >= 1)
|
||||
{
|
||||
List<int> users = new();
|
||||
foreach (string playerUsername in createRoom.Players)
|
||||
case UpdateMyPlayerData playerData:
|
||||
{
|
||||
User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||
if (player != null) users.Add(player.UserId);
|
||||
else return this.BadRequest();
|
||||
MatchHelper.SetUserLocation(user.UserId, token.UserLocation);
|
||||
Room? room = RoomHelper.FindRoomByUser(user.UserId, token.GameVersion, token.Platform, true);
|
||||
|
||||
if (playerData.RoomState != null)
|
||||
if (room != null && Equals(room.HostId, user.UserId))
|
||||
room.State = (RoomState)playerData.RoomState;
|
||||
break;
|
||||
}
|
||||
|
||||
// Create a new one as requested
|
||||
RoomHelper.CreateRoom(users, gameToken.GameVersion, gameToken.Platform, createRoom.RoomSlot);
|
||||
}
|
||||
|
||||
else if (matchData is UpdatePlayersInRoom updatePlayersInRoom)
|
||||
{
|
||||
Room? room = RoomHelper.Rooms.FirstOrDefault(r => r.HostId == user.UserId);
|
||||
|
||||
if (room != null)
|
||||
// Check how many people are online in release builds, disabled for debug for ..well debugging.
|
||||
#if DEBUG
|
||||
case FindBestRoom diveInData:
|
||||
#else
|
||||
case FindBestRoom diveInData when MatchHelper.UserLocations.Count > 1:
|
||||
#endif
|
||||
{
|
||||
List<User> users = new();
|
||||
foreach (string playerUsername in updatePlayersInRoom.Players)
|
||||
FindBestRoomResponse? response = RoomHelper.FindBestRoom
|
||||
(user, token.GameVersion, diveInData.RoomSlot, token.Platform, token.UserLocation);
|
||||
|
||||
if (response == null) return this.NotFound();
|
||||
|
||||
string serialized = JsonSerializer.Serialize(response, typeof(FindBestRoomResponse));
|
||||
foreach (Player player in response.Players) MatchHelper.AddUserRecentlyDivedIn(user.UserId, player.User.UserId);
|
||||
|
||||
return this.Ok($"[{{\"StatusCode\":200}},{serialized}]");
|
||||
}
|
||||
case CreateRoom createRoom when MatchHelper.UserLocations.Count >= 1:
|
||||
{
|
||||
List<int> users = new();
|
||||
foreach (string playerUsername in createRoom.Players)
|
||||
{
|
||||
User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||
if (player != null) users.Add(player);
|
||||
if (player != null) users.Add(player.UserId);
|
||||
else return this.BadRequest();
|
||||
}
|
||||
|
||||
room.PlayerIds = users.Select(u => u.UserId).ToList();
|
||||
await RoomHelper.CleanupRooms(null, room);
|
||||
// Create a new one as requested
|
||||
RoomHelper.CreateRoom(users, token.GameVersion, token.Platform, createRoom.RoomSlot);
|
||||
break;
|
||||
}
|
||||
case UpdatePlayersInRoom updatePlayersInRoom:
|
||||
{
|
||||
Room? room = RoomHelper.Rooms.FirstOrDefault(r => r.HostId == user.UserId);
|
||||
|
||||
if (room != null)
|
||||
{
|
||||
List<User> users = new();
|
||||
foreach (string playerUsername in updatePlayersInRoom.Players)
|
||||
{
|
||||
User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||
if (player != null) users.Add(player);
|
||||
else return this.BadRequest();
|
||||
}
|
||||
|
||||
room.PlayerIds = users.Select(u => u.UserId).ToList();
|
||||
await RoomHelper.CleanupRooms(null, room);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
#nullable enable
|
||||
using System.Globalization;
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/plain")]
|
||||
public class MessageController : ControllerBase
|
||||
|
@ -35,20 +38,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.";
|
|||
}
|
||||
|
||||
[HttpGet("eula")]
|
||||
public async Task<IActionResult> Eula()
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
// ReSharper disable once ConvertIfStatementToReturnStatement
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
return this.Ok($"{license}\n{ServerConfiguration.Instance.EulaText}");
|
||||
}
|
||||
public IActionResult Eula() => this.Ok($"{license}\n{ServerConfiguration.Instance.EulaText}");
|
||||
|
||||
[HttpGet("announce")]
|
||||
public async Task<IActionResult> Announce()
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
string username = await this.database.UsernameFromGameToken(token);
|
||||
|
||||
|
@ -76,16 +71,15 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.";
|
|||
|
||||
[HttpGet("notification")]
|
||||
public IActionResult Notification() => this.Ok();
|
||||
|
||||
/// <summary>
|
||||
/// Filters chat messages sent by a user.
|
||||
/// The reponse sent is the text that will appear in-game.
|
||||
/// The response sent is the text that will appear in-game.
|
||||
/// </summary>
|
||||
[HttpPost("filter")]
|
||||
public async Task<IActionResult> Filter()
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
string response = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
#nullable enable
|
||||
using System.Text.Json;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Administration.Reports;
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/xml")]
|
||||
public class ReportController : ControllerBase
|
||||
|
@ -25,15 +27,11 @@ public class ReportController : ControllerBase
|
|||
[HttpPost("grief")]
|
||||
public async Task<IActionResult> Report()
|
||||
{
|
||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
this.Request.Body.Position = 0;
|
||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
||||
XmlSerializer serializer = new(typeof(GriefReport));
|
||||
GriefReport? report = (GriefReport?)serializer.Deserialize(new StringReader(bodyString));
|
||||
string username = await this.database.UsernameFromGameToken(token);
|
||||
|
||||
GriefReport? report = await this.DeserializeBody<GriefReport>();
|
||||
if (report == null) return this.BadRequest();
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(report);
|
||||
|
@ -41,14 +39,14 @@ public class ReportController : ControllerBase
|
|||
report.Bounds = JsonSerializer.Serialize(report.XmlBounds.Rect, typeof(Rectangle));
|
||||
report.Players = JsonSerializer.Serialize(report.XmlPlayers, typeof(ReportPlayer[]));
|
||||
report.Timestamp = TimeHelper.UnixTimeMilliseconds();
|
||||
report.ReportingPlayerId = user.UserId;
|
||||
report.ReportingPlayerId = token.UserId;
|
||||
|
||||
this.database.Reports.Add(report);
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
await WebhookHelper.SendWebhook(
|
||||
title: "New grief report",
|
||||
description: $"Submitted by {user.Username}\n" +
|
||||
description: $"Submitted by {username}\n" +
|
||||
$"To view it, click [here]({ServerConfiguration.Instance.ExternalUrl}/moderation/report/{report.ReportId}).",
|
||||
dest: WebhookHelper.WebhookDestination.Moderation
|
||||
);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#nullable enable
|
||||
using System.Xml.Serialization;
|
||||
using Discord;
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
|
@ -9,12 +8,14 @@ using LBPUnion.ProjectLighthouse.Logging;
|
|||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Resources;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/xml")]
|
||||
public class PhotosController : ControllerBase
|
||||
|
@ -29,16 +30,12 @@ public class PhotosController : ControllerBase
|
|||
[HttpPost("uploadPhoto")]
|
||||
public async Task<IActionResult> UploadPhoto()
|
||||
{
|
||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||
User? user = await this.database.UserFromGameToken(this.GetToken());
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
if (user.PhotosByMe >= ServerConfiguration.Instance.UserGeneratedContentLimits.PhotosQuota) return this.BadRequest();
|
||||
|
||||
this.Request.Body.Position = 0;
|
||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
||||
XmlSerializer serializer = new(typeof(Photo));
|
||||
Photo? photo = (Photo?)serializer.Deserialize(new StringReader(bodyString));
|
||||
Photo? photo = await this.DeserializeBody<Photo>();
|
||||
if (photo == null) return this.BadRequest();
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(photo);
|
||||
|
@ -65,9 +62,9 @@ public class PhotosController : ControllerBase
|
|||
{
|
||||
// We'll grab the slot by the RootLevel and see what happens from here.
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == SlotType.User && s.ResourceCollection.Contains(photoSlot.RootLevel));
|
||||
if(slot == null) break;
|
||||
if (slot == null) break;
|
||||
|
||||
if (!string.IsNullOrEmpty(slot!.RootLevel)) validLevel = true;
|
||||
if (!string.IsNullOrEmpty(slot.RootLevel)) validLevel = true;
|
||||
if (slot.IsAdventurePlanet) photoSlot.SlotId = slot.SlotId;
|
||||
break;
|
||||
}
|
||||
|
@ -83,6 +80,10 @@ public class PhotosController : ControllerBase
|
|||
validLevel = true;
|
||||
break;
|
||||
}
|
||||
case SlotType.Moon:
|
||||
case SlotType.Unknown:
|
||||
case SlotType.Unknown2:
|
||||
case SlotType.DLC:
|
||||
default: Logger.Warn($"Invalid photo level type: {photoSlot.SlotType}", LogArea.Photos);
|
||||
break;
|
||||
}
|
||||
|
@ -103,10 +104,8 @@ public class PhotosController : ControllerBase
|
|||
subjectUserIds.Add(subject.Username);
|
||||
}
|
||||
|
||||
foreach (PhotoSubject subject in photo.Subjects)
|
||||
foreach (PhotoSubject subject in photo.Subjects.Where(subject => !string.IsNullOrEmpty(subject.Username)))
|
||||
{
|
||||
if (string.IsNullOrEmpty(subject.Username)) continue;
|
||||
|
||||
subject.User = await this.database.Users.FirstOrDefaultAsync(u => u.Username == subject.Username);
|
||||
|
||||
if (subject.User == null) continue;
|
||||
|
@ -144,9 +143,6 @@ public class PhotosController : ControllerBase
|
|||
[HttpGet("photos/{slotType}/{id:int}")]
|
||||
public async Task<IActionResult> SlotPhotos([FromQuery] int pageStart, [FromQuery] int pageSize, string slotType, int id)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();
|
||||
|
@ -166,12 +162,9 @@ public class PhotosController : ControllerBase
|
|||
[HttpGet("photos/by")]
|
||||
public async Task<IActionResult> UserPhotosBy([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
int targetUserId = await this.database.Users.Where(u => u.Username == user).Select(u => u.UserId).FirstOrDefaultAsync();
|
||||
int targetUserId = await this.database.UserIdFromUsername(user);
|
||||
if (targetUserId == 0) return this.NotFound();
|
||||
|
||||
List<Photo> photos = await this.database.Photos.Include
|
||||
|
@ -188,12 +181,9 @@ public class PhotosController : ControllerBase
|
|||
[HttpGet("photos/with")]
|
||||
public async Task<IActionResult> UserPhotosWith([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
int targetUserId = await this.database.Users.Where(u => u.Username == user).Select(u => u.UserId).FirstOrDefaultAsync();
|
||||
int targetUserId = await this.database.UserIdFromUsername(user);
|
||||
if (targetUserId == 0) return this.NotFound();
|
||||
|
||||
List<int> photoSubjectIds = new();
|
||||
|
@ -220,8 +210,7 @@ public class PhotosController : ControllerBase
|
|||
[HttpPost("deletePhoto/{id:int}")]
|
||||
public async Task<IActionResult> DeletePhoto(int id)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
Photo? photo = await this.database.Photos.FirstOrDefaultAsync(p => p.PhotoId == id);
|
||||
if (photo == null) return this.NotFound();
|
||||
|
|
|
@ -1,28 +1,23 @@
|
|||
#nullable enable
|
||||
using System.Buffers;
|
||||
using System.IO.Pipelines;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Files;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using IOFile = System.IO.File;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Resources;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Produces("text/xml")]
|
||||
[Route("LITTLEBIGPLANETPS3_XML")]
|
||||
public class ResourcesController : ControllerBase
|
||||
{
|
||||
private readonly Database database;
|
||||
|
||||
public ResourcesController(Database database)
|
||||
{
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
[HttpPost("showModerated")]
|
||||
public IActionResult ShowModerated() => this.Ok(LbpSerializer.BlankElement("resources"));
|
||||
|
@ -31,14 +26,7 @@ public class ResourcesController : ControllerBase
|
|||
[HttpPost("showNotUploaded")]
|
||||
public async Task<IActionResult> FilterResources()
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
||||
XmlSerializer serializer = new(typeof(ResourceList));
|
||||
ResourceList? resourceList = (ResourceList?)serializer.Deserialize(new StringReader(bodyString));
|
||||
|
||||
ResourceList? resourceList = await this.DeserializeBody<ResourceList>();
|
||||
if (resourceList == null) return this.BadRequest();
|
||||
|
||||
string resources = resourceList.Resources.Where
|
||||
|
@ -49,18 +37,14 @@ public class ResourcesController : ControllerBase
|
|||
}
|
||||
|
||||
[HttpGet("r/{hash}")]
|
||||
public async Task<IActionResult> GetResource(string hash)
|
||||
public IActionResult GetResource(string hash)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
string path = FileHelper.GetResourcePath(hash);
|
||||
|
||||
string fullPath = Path.GetFullPath(path);
|
||||
string basePath = Path.GetFullPath(FileHelper.ResourcePath);
|
||||
|
||||
// Prevent directory traversal attacks
|
||||
if (!fullPath.StartsWith(basePath)) return this.BadRequest();
|
||||
if (!fullPath.StartsWith(FileHelper.FullResourcePath)) return this.BadRequest();
|
||||
|
||||
if (FileHelper.ResourceExists(hash)) return this.File(IOFile.OpenRead(path), "application/octet-stream");
|
||||
|
||||
|
@ -72,16 +56,17 @@ public class ResourcesController : ControllerBase
|
|||
[HttpPost("upload/{hash}")]
|
||||
public async Task<IActionResult> UploadResource(string hash)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
string assetsDirectory = FileHelper.ResourcePath;
|
||||
string path = FileHelper.GetResourcePath(hash);
|
||||
string fullPath = Path.GetFullPath(path);
|
||||
|
||||
FileHelper.EnsureDirectoryCreated(assetsDirectory);
|
||||
// lbp treats code 409 as success and as an indicator that the file is already present
|
||||
if (FileHelper.ResourceExists(hash)) return this.Conflict();
|
||||
|
||||
// theoretically shouldn't be possible because of hash check but handle anyways
|
||||
if (!fullPath.StartsWith(FileHelper.FullResourcePath)) return this.BadRequest();
|
||||
|
||||
Logger.Info($"Processing resource upload (hash: {hash})", LogArea.Resources);
|
||||
LbpFile file = new(await readFromPipeReader(this.Request.BodyReader));
|
||||
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
#nullable enable
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Levels.Categories;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/xml")]
|
||||
public class CollectionController : ControllerBase
|
||||
|
@ -28,9 +29,6 @@ public class CollectionController : ControllerBase
|
|||
[HttpGet("playlists/{playlistId:int}/slots")]
|
||||
public async Task<IActionResult> GetPlaylistSlots(int playlistId)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
Playlist? targetPlaylist = await this.database.Playlists.FirstOrDefaultAsync(p => p.PlaylistId == playlistId);
|
||||
if (targetPlaylist == null) return this.BadRequest();
|
||||
|
||||
|
@ -50,8 +48,7 @@ public class CollectionController : ControllerBase
|
|||
[HttpPost("playlists/{playlistId:int}/order_slots")]
|
||||
public async Task<IActionResult> UpdatePlaylist(int playlistId, int slotId)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
Playlist? targetPlaylist = await this.database.Playlists.FirstOrDefaultAsync(p => p.PlaylistId == playlistId);
|
||||
if (targetPlaylist == null) return this.BadRequest();
|
||||
|
@ -66,7 +63,7 @@ public class CollectionController : ControllerBase
|
|||
return this.Ok(this.GetUserPlaylists(token.UserId));
|
||||
}
|
||||
|
||||
Playlist? newPlaylist = await this.getPlaylistFromBody();
|
||||
Playlist? newPlaylist = await this.DeserializeBody<Playlist>("playlist", "levels");
|
||||
|
||||
if (newPlaylist == null) return this.BadRequest();
|
||||
|
||||
|
@ -116,14 +113,13 @@ public class CollectionController : ControllerBase
|
|||
[HttpPost("playlists")]
|
||||
public async Task<IActionResult> CreatePlaylist()
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
int playlistCount = await this.database.Playlists.CountAsync(p => p.CreatorId == token.UserId);
|
||||
|
||||
if (playlistCount > ServerConfiguration.Instance.UserGeneratedContentLimits.ListsQuota) return this.BadRequest();
|
||||
|
||||
Playlist? playlist = await this.getPlaylistFromBody();
|
||||
Playlist? playlist = await this.DeserializeBody<Playlist>("playlist");
|
||||
|
||||
if (playlist == null) return this.BadRequest();
|
||||
|
||||
|
@ -139,10 +135,7 @@ public class CollectionController : ControllerBase
|
|||
[HttpGet("user/{username}/playlists")]
|
||||
public async Task<IActionResult> GetUserPlaylists(string username)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
int targetUserId = await this.database.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync();
|
||||
int targetUserId = await this.database.UserIdFromUsername(username);
|
||||
if (targetUserId == 0) return this.BadRequest();
|
||||
|
||||
return this.Ok(this.GetUserPlaylists(targetUserId));
|
||||
|
@ -152,8 +145,9 @@ public class CollectionController : ControllerBase
|
|||
[HttpGet("genres")]
|
||||
public async Task<IActionResult> GenresAndSearches()
|
||||
{
|
||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
|
||||
string categoriesSerialized = CategoryHelper.Categories.Aggregate
|
||||
(
|
||||
|
@ -196,13 +190,9 @@ public class CollectionController : ControllerBase
|
|||
[HttpGet("searches/{endpointName}")]
|
||||
public async Task<IActionResult> GetCategorySlots(string endpointName, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (userAndToken == null) return this.StatusCode(403, "");
|
||||
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
User user = userAndToken.Value.Item1;
|
||||
GameToken gameToken = userAndToken.Value.Item2;
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
|
||||
Category? category = CategoryHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName);
|
||||
if (category == null) return this.NotFound();
|
||||
|
@ -223,7 +213,7 @@ public class CollectionController : ControllerBase
|
|||
totalSlots = category.GetTotalSlots(this.database);
|
||||
}
|
||||
|
||||
string slotsSerialized = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(gameToken.GameVersion));
|
||||
string slotsSerialized = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize(token.GameVersion));
|
||||
|
||||
return this.Ok
|
||||
(
|
||||
|
@ -243,19 +233,4 @@ public class CollectionController : ControllerBase
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
private async Task<Playlist?> getPlaylistFromBody()
|
||||
{
|
||||
this.Request.Body.Position = 0;
|
||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
||||
string rootElement = bodyString.StartsWith("<playlist>") ? "playlist" : "levels";
|
||||
XmlSerializer serializer = new(typeof(Playlist), new XmlRootAttribute(rootElement));
|
||||
Playlist? playlist = (Playlist?)serializer.Deserialize(new StringReader(bodyString));
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(playlist);
|
||||
|
||||
return playlist;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,12 +1,15 @@
|
|||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Levels;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML")]
|
||||
[Produces("text/plain")]
|
||||
public class LevelTagsController : ControllerBase
|
||||
|
@ -34,15 +37,14 @@ public class LevelTagsController : ControllerBase
|
|||
}
|
||||
|
||||
[HttpPost("tag/{slotType}/{id:int}")]
|
||||
public async Task<IActionResult> PostTag([FromForm] string t, [FromRoute] string slotType, [FromRoute] int id)
|
||||
public async Task<IActionResult> PostTag([FromForm(Name = "t")] string tagName, [FromRoute] string slotType, [FromRoute] int id)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
Slot? slot = await this.database.Slots.Where(s => s.SlotId == id).FirstOrDefaultAsync();
|
||||
if (slot == null) return this.BadRequest();
|
||||
|
||||
if (!LabelHelper.IsValidTag(t)) return this.BadRequest();
|
||||
if (!LabelHelper.IsValidTag(tagName)) return this.BadRequest();
|
||||
|
||||
if (token.UserId == slot.CreatorId) return this.BadRequest();
|
||||
|
||||
|
@ -53,7 +55,7 @@ public class LevelTagsController : ControllerBase
|
|||
RatedLevel? rating = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.UserId == token.UserId && r.SlotId == slot.SlotId);
|
||||
if (rating == null) return this.BadRequest();
|
||||
|
||||
rating.TagLBP1 = t;
|
||||
rating.TagLBP1 = tagName;
|
||||
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
|
|
|
@ -5,12 +5,14 @@ using LBPUnion.ProjectLighthouse.Levels;
|
|||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/xml")]
|
||||
public class ListController : ControllerBase
|
||||
|
@ -37,8 +39,7 @@ public class ListController : ControllerBase
|
|||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -64,8 +65,7 @@ public class ListController : ControllerBase
|
|||
[HttpPost("lolcatftw/add/user/{id:int}")]
|
||||
public async Task<IActionResult> AddQueuedLevel(int id)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
if (slot == null) return this.NotFound();
|
||||
|
@ -78,8 +78,7 @@ public class ListController : ControllerBase
|
|||
[HttpPost("lolcatftw/remove/user/{id:int}")]
|
||||
public async Task<IActionResult> RemoveQueuedLevel(int id)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
if (slot == null) return this.NotFound();
|
||||
|
@ -92,8 +91,7 @@ public class ListController : ControllerBase
|
|||
[HttpPost("lolcatftw/clear")]
|
||||
public async Task<IActionResult> ClearQueuedLevels()
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
this.database.QueuedLevels.RemoveRange(this.database.QueuedLevels.Where(q => q.UserId == token.UserId));
|
||||
|
||||
|
@ -118,8 +116,7 @@ public class ListController : ControllerBase
|
|||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -150,8 +147,7 @@ public class ListController : ControllerBase
|
|||
[HttpPost("favourite/slot/{slotType}/{id:int}")]
|
||||
public async Task<IActionResult> AddFavouriteSlot(string slotType, int id)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();
|
||||
|
||||
|
@ -174,8 +170,7 @@ public class ListController : ControllerBase
|
|||
[HttpPost("unfavourite/slot/{slotType}/{id:int}")]
|
||||
public async Task<IActionResult> RemoveFavouriteSlot(string slotType, int id)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (SlotHelper.IsTypeInvalid(slotType)) return this.BadRequest();
|
||||
|
||||
|
@ -202,12 +197,9 @@ public class ListController : ControllerBase
|
|||
[HttpGet("favouritePlaylists/{username}")]
|
||||
public async Task<IActionResult> GetFavouritePlaylists(string username, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
int targetUserId = await this.database.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync();
|
||||
int targetUserId = await this.database.UserIdFromUsername(username);
|
||||
if (targetUserId == 0) return this.StatusCode(403, "");
|
||||
|
||||
IEnumerable<Playlist> heartedPlaylists = this.database.HeartedPlaylists.Where(p => p.UserId == targetUserId)
|
||||
|
@ -228,8 +220,7 @@ public class ListController : ControllerBase
|
|||
[HttpPost("favourite/playlist/{playlistId:int}")]
|
||||
public async Task<IActionResult> AddFavouritePlaylist(int playlistId)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
Playlist? playlist = await this.database.Playlists.FirstOrDefaultAsync(s => s.PlaylistId == playlistId);
|
||||
if (playlist == null) return this.NotFound();
|
||||
|
@ -242,8 +233,7 @@ public class ListController : ControllerBase
|
|||
[HttpPost("unfavourite/playlist/{playlistId:int}")]
|
||||
public async Task<IActionResult> RemoveFavouritePlaylist(int playlistId)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
Playlist? playlist = await this.database.Playlists.FirstOrDefaultAsync(s => s.PlaylistId == playlistId);
|
||||
if (playlist == null) return this.NotFound();
|
||||
|
@ -262,8 +252,7 @@ public class ListController : ControllerBase
|
|||
[HttpGet("favouriteUsers/{username}")]
|
||||
public async Task<IActionResult> GetFavouriteUsers(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
User? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
if (targetUser == null) return this.StatusCode(403, "");
|
||||
|
@ -295,8 +284,7 @@ public class ListController : ControllerBase
|
|||
[HttpPost("favourite/user/{username}")]
|
||||
public async Task<IActionResult> AddFavouriteUser(string username)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
if (heartedUser == null) return this.NotFound();
|
||||
|
@ -309,8 +297,7 @@ public class ListController : ControllerBase
|
|||
[HttpPost("unfavourite/user/{username}")]
|
||||
public async Task<IActionResult> RemoveFavouriteUser(string username)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
if (heartedUser == null) return this.NotFound();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#nullable enable
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Files;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Levels;
|
||||
|
@ -8,12 +8,14 @@ using LBPUnion.ProjectLighthouse.Logging;
|
|||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/xml")]
|
||||
public class PublishController : ControllerBase
|
||||
|
@ -31,15 +33,12 @@ public class PublishController : ControllerBase
|
|||
[HttpPost("startPublish")]
|
||||
public async Task<IActionResult> StartPublish()
|
||||
{
|
||||
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (userAndToken == null) return this.StatusCode(403, "");
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
User user = userAndToken.Value.Item1;
|
||||
GameToken gameToken = userAndToken.Value.Item2;
|
||||
|
||||
Slot? slot = await this.getSlotFromBody();
|
||||
Slot? slot = await this.DeserializeBody<Slot>();
|
||||
if (slot == null)
|
||||
{
|
||||
Logger.Warn("Rejecting level upload, slot is null", LogArea.Publish);
|
||||
|
@ -69,7 +68,7 @@ public class PublishController : ControllerBase
|
|||
return this.BadRequest();
|
||||
}
|
||||
}
|
||||
else if (user.GetUsedSlotsForGame(gameToken.GameVersion) > user.EntitledSlots)
|
||||
else if (user.GetUsedSlotsForGame(token.GameVersion) > user.EntitledSlots)
|
||||
{
|
||||
return this.StatusCode(403, "");
|
||||
}
|
||||
|
@ -89,14 +88,12 @@ public class PublishController : ControllerBase
|
|||
[HttpPost("publish")]
|
||||
public async Task<IActionResult> Publish([FromQuery] string? game)
|
||||
{
|
||||
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (userAndToken == null) return this.StatusCode(403, "");
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
User user = userAndToken.Value.Item1;
|
||||
GameToken gameToken = userAndToken.Value.Item2;
|
||||
Slot? slot = await this.getSlotFromBody();
|
||||
Slot? slot = await this.DeserializeBody<Slot>();
|
||||
|
||||
if (slot == null)
|
||||
{
|
||||
|
@ -156,7 +153,7 @@ public class PublishController : ControllerBase
|
|||
GameVersion slotVersion = FileHelper.ParseLevelVersion(rootLevel);
|
||||
|
||||
slot.GameVersion = slotVersion;
|
||||
if (slotVersion == GameVersion.Unknown) slot.GameVersion = gameToken.GameVersion;
|
||||
if (slotVersion == GameVersion.Unknown) slot.GameVersion = token.GameVersion;
|
||||
|
||||
slot.AuthorLabels = LabelHelper.RemoveInvalidLabels(slot.AuthorLabels);
|
||||
|
||||
|
@ -185,7 +182,7 @@ public class PublishController : ControllerBase
|
|||
if (intendedVersion != GameVersion.Unknown && intendedVersion != slotVersion)
|
||||
{
|
||||
// Delete the useless rootLevel that lbp3 just uploaded
|
||||
if(slotVersion == GameVersion.LittleBigPlanet3)
|
||||
if (slotVersion == GameVersion.LittleBigPlanet3)
|
||||
FileHelper.DeleteResource(slot.RootLevel);
|
||||
|
||||
slot.GameVersion = oldSlot.GameVersion;
|
||||
|
@ -230,7 +227,7 @@ public class PublishController : ControllerBase
|
|||
|
||||
this.database.Entry(oldSlot).CurrentValues.SetValues(slot);
|
||||
await this.database.SaveChangesAsync();
|
||||
return this.Ok(oldSlot.Serialize(gameToken.GameVersion));
|
||||
return this.Ok(oldSlot.Serialize(token.GameVersion));
|
||||
}
|
||||
|
||||
if (user.GetUsedSlotsForGame(slotVersion) > user.EntitledSlots)
|
||||
|
@ -269,14 +266,13 @@ public class PublishController : ControllerBase
|
|||
|
||||
Logger.Success($"Successfully published level {slot.Name} (id: {slot.SlotId}) by {user.Username} (id: {user.UserId})", LogArea.Publish);
|
||||
|
||||
return this.Ok(slot.Serialize(gameToken.GameVersion));
|
||||
return this.Ok(slot.Serialize(token.GameVersion));
|
||||
}
|
||||
|
||||
[HttpPost("unpublish/{id:int}")]
|
||||
public async Task<IActionResult> Unpublish(int id)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
Slot? slot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
if (slot == null) return this.NotFound();
|
||||
|
@ -305,17 +301,4 @@ public class PublishController : ControllerBase
|
|||
_ => GameVersion.Unknown,
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<Slot?> getSlotFromBody()
|
||||
{
|
||||
this.Request.Body.Position = 0;
|
||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
||||
XmlSerializer serializer = new(typeof(Slot));
|
||||
Slot? slot = (Slot?)serializer.Deserialize(new StringReader(bodyString));
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(slot);
|
||||
|
||||
return slot;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
#nullable enable
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Administration;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
@ -7,12 +6,14 @@ using LBPUnion.ProjectLighthouse.Levels;
|
|||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Reviews;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/xml")]
|
||||
public class ReviewController : ControllerBase
|
||||
|
@ -28,8 +29,7 @@ public class ReviewController : ControllerBase
|
|||
[HttpPost("rate/user/{slotId:int}")]
|
||||
public async Task<IActionResult> Rate(int slotId, [FromQuery] int rating)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
Slot? slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
if (slot == null) return this.StatusCode(403, "");
|
||||
|
@ -58,8 +58,7 @@ public class ReviewController : ControllerBase
|
|||
[HttpPost("dpadrate/user/{slotId:int}")]
|
||||
public async Task<IActionResult> DPadRate(int slotId, [FromQuery] int rating)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
Slot? slot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
if (slot == null) return this.StatusCode(403, "");
|
||||
|
@ -90,10 +89,9 @@ public class ReviewController : ControllerBase
|
|||
[HttpPost("postReview/user/{slotId:int}")]
|
||||
public async Task<IActionResult> PostReview(int slotId)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
Review? newReview = await this.getReviewFromBody();
|
||||
Review? newReview = await this.DeserializeBody<Review>();
|
||||
if (newReview == null) return this.BadRequest();
|
||||
|
||||
if (newReview.Text.Length > 512) return this.BadRequest();
|
||||
|
@ -143,8 +141,7 @@ public class ReviewController : ControllerBase
|
|||
[HttpGet("reviewsFor/user/{slotId:int}")]
|
||||
public async Task<IActionResult> ReviewsFor(int slotId, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -195,14 +192,13 @@ public class ReviewController : ControllerBase
|
|||
[HttpGet("reviewsBy/{username}")]
|
||||
public async Task<IActionResult> ReviewsBy(string username, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
int targetUserId = await this.database.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync();
|
||||
int targetUserId = await this.database.UserIdFromUsername(username);
|
||||
|
||||
if (targetUserId == 0) return this.BadRequest();
|
||||
|
||||
|
@ -249,10 +245,9 @@ public class ReviewController : ControllerBase
|
|||
[HttpPost("rateReview/user/{slotId:int}/{username}")]
|
||||
public async Task<IActionResult> RateReview(int slotId, string username, [FromQuery] int rating = 0)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
int reviewerId = await this.database.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync();
|
||||
int reviewerId = await this.database.UserIdFromUsername(username);
|
||||
if (reviewerId == 0) return this.StatusCode(400, "");
|
||||
|
||||
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewerId);
|
||||
|
@ -303,15 +298,14 @@ public class ReviewController : ControllerBase
|
|||
[HttpPost("deleteReview/user/{slotId:int}/{username}")]
|
||||
public async Task<IActionResult> DeleteReview(int slotId, string username)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
int creatorId = await this.database.Slots.Where(s => s.SlotId == slotId).Select(s => s.CreatorId).FirstOrDefaultAsync();
|
||||
if (creatorId == 0) return this.StatusCode(400, "");
|
||||
|
||||
if (token.UserId != creatorId) return this.StatusCode(403, "");
|
||||
|
||||
int reviewerId = await this.database.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync();
|
||||
int reviewerId = await this.database.UserIdFromUsername(username);
|
||||
if (reviewerId == 0) return this.StatusCode(400, "");
|
||||
|
||||
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewerId);
|
||||
|
@ -323,15 +317,4 @@ public class ReviewController : ControllerBase
|
|||
await this.database.SaveChangesAsync();
|
||||
return this.Ok();
|
||||
}
|
||||
|
||||
private async Task<Review?> getReviewFromBody()
|
||||
{
|
||||
this.Request.Body.Position = 0;
|
||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
||||
XmlSerializer serializer = new(typeof(Review));
|
||||
Review? review = (Review?)serializer.Deserialize(new StringReader(bodyString));
|
||||
SanitizationHelper.SanitizeStringsInClass(review);
|
||||
return review;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#nullable enable
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
|
@ -30,8 +31,7 @@ public class ScoreController : ControllerBase
|
|||
[HttpPost("scoreboard/{slotType}/{id:int}/{childId:int}")]
|
||||
public async Task<IActionResult> SubmitScore(string slotType, int id, int childId, [FromQuery] bool lbp1 = false, [FromQuery] bool lbp2 = false, [FromQuery] bool lbp3 = false)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
string username = await this.database.UsernameFromGameToken(token);
|
||||
|
||||
|
@ -41,11 +41,7 @@ public class ScoreController : ControllerBase
|
|||
return this.BadRequest();
|
||||
}
|
||||
|
||||
this.Request.Body.Position = 0;
|
||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
||||
XmlSerializer serializer = new(typeof(Score));
|
||||
Score? score = (Score?)serializer.Deserialize(new StringReader(bodyString));
|
||||
Score? score = await this.DeserializeBody<Score>();
|
||||
if (score == null)
|
||||
{
|
||||
Logger.Warn($"Rejecting score upload, score is null (slotType={slotType}, slotId={id}, user={username})", LogArea.Score);
|
||||
|
@ -157,8 +153,7 @@ public class ScoreController : ControllerBase
|
|||
[HttpGet("friendscores/{slotType}/{slotId:int}/{childId:int}/{type:int}")]
|
||||
public async Task<IActionResult> FriendScores(string slotType, int slotId, int? childId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -189,8 +184,7 @@ public class ScoreController : ControllerBase
|
|||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||
public async Task<IActionResult> TopScores(string slotType, int slotId, int? childId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
|
|
@ -3,12 +3,14 @@ using LBPUnion.ProjectLighthouse.Extensions;
|
|||
using LBPUnion.ProjectLighthouse.Levels;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/slots")]
|
||||
[Produces("text/xml")]
|
||||
public class SearchController : ControllerBase
|
||||
|
@ -31,8 +33,7 @@ public class SearchController : ControllerBase
|
|||
string? keyName = "slots"
|
||||
)
|
||||
{
|
||||
GameToken? gameToken = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (gameToken == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -42,7 +43,7 @@ public class SearchController : ControllerBase
|
|||
|
||||
string[] keywords = query.Split(" ");
|
||||
|
||||
IQueryable<Slot> dbQuery = this.database.Slots.ByGameVersion(gameToken.GameVersion, false, true)
|
||||
IQueryable<Slot> dbQuery = this.database.Slots.ByGameVersion(token.GameVersion, false, true)
|
||||
.Where(s => s.Type == SlotType.User)
|
||||
.OrderBy(s => !s.TeamPick)
|
||||
.ThenByDescending(s => s.FirstUploaded)
|
||||
|
@ -60,7 +61,7 @@ public class SearchController : ControllerBase
|
|||
|
||||
List<Slot> slots = await dbQuery.Skip(Math.Max(0, pageStart - 1)).Take(Math.Min(pageSize, 30)).ToListAsync();
|
||||
|
||||
string response = slots.Aggregate("", (current, slot) => current + slot.Serialize(gameToken.GameVersion));
|
||||
string response = slots.Aggregate("", (current, slot) => current + slot.Serialize(token.GameVersion));
|
||||
|
||||
return this.Ok(LbpSerializer.TaggedStringElement(keyName, response, "total", dbQuery.Count()));
|
||||
}
|
||||
|
|
|
@ -8,12 +8,14 @@ using LBPUnion.ProjectLighthouse.PlayerData;
|
|||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Reviews;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/xml")]
|
||||
public class SlotsController : ControllerBase
|
||||
|
@ -38,38 +40,36 @@ public class SlotsController : ControllerBase
|
|||
});
|
||||
|
||||
[HttpGet("slots/by")]
|
||||
public async Task<IActionResult> SlotsBy([FromQuery] string u, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
public async Task<IActionResult> SlotsBy([FromQuery(Name="u")] string username, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
User? targetUser = await this.database.Users.Where(dbUser => dbUser.Username == u).FirstOrDefaultAsync();
|
||||
if (targetUser == null) return this.NotFound();
|
||||
int targetUserId = await this.database.UserIdFromUsername(username);
|
||||
if (targetUserId == 0) return this.NotFound();
|
||||
|
||||
int usedSlots = this.database.Slots.Count(s => s.CreatorId == targetUserId);
|
||||
|
||||
string response = Enumerable.Aggregate
|
||||
(
|
||||
this.database.Slots.ByGameVersion(gameVersion, token.UserId == targetUser.UserId, true)
|
||||
.Where(s => s.CreatorId == targetUser.UserId)
|
||||
this.database.Slots.Where(s => s.CreatorId == targetUserId)
|
||||
.ByGameVersion(gameVersion, token.UserId == targetUserId, true)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, targetUser.UsedSlots)),
|
||||
.Take(Math.Min(pageSize, usedSlots)),
|
||||
string.Empty,
|
||||
(current, slot) => current + slot.Serialize(token.GameVersion)
|
||||
);
|
||||
int start = pageStart + Math.Min(pageSize, targetUser.UsedSlots);
|
||||
int total = await this.database.Slots.CountAsync(s => s.CreatorId == targetUser.UserId);
|
||||
int start = pageStart + Math.Min(pageSize, usedSlots);
|
||||
int total = await this.database.Slots.CountAsync(s => s.CreatorId == targetUserId);
|
||||
return this.Ok(generateSlotsResponse(response, start, total));
|
||||
}
|
||||
|
||||
[HttpGet("slotList")]
|
||||
public async Task<IActionResult> GetSlotListAlt([FromQuery] int[] s)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
List<string?> serializedSlots = new();
|
||||
foreach (int slotId in s)
|
||||
{
|
||||
|
@ -93,9 +93,6 @@ public class SlotsController : ControllerBase
|
|||
[HttpGet("slots/developer")]
|
||||
public async Task<IActionResult> StoryPlayers()
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
List<int> activeSlotIds = RoomHelper.Rooms.Where(r => r.Slot.SlotType == SlotType.Developer).Select(r => r.Slot.SlotId).ToList();
|
||||
|
||||
List<string> serializedSlots = new();
|
||||
|
@ -115,9 +112,6 @@ public class SlotsController : ControllerBase
|
|||
[HttpGet("s/developer/{id:int}")]
|
||||
public async Task<IActionResult> SDev(int id)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
int slotId = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
|
||||
Slot slot = await this.database.Slots.FirstAsync(s => s.SlotId == slotId);
|
||||
|
||||
|
@ -127,8 +121,7 @@ public class SlotsController : ControllerBase
|
|||
[HttpGet("s/user/{id:int}")]
|
||||
public async Task<IActionResult> SUser(int id)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
|
@ -168,8 +161,7 @@ public class SlotsController : ControllerBase
|
|||
[HttpGet("slots")]
|
||||
public async Task<IActionResult> NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -189,8 +181,7 @@ public class SlotsController : ControllerBase
|
|||
[HttpGet("slots/like/{slotType}/{slotId:int}")]
|
||||
public async Task<IActionResult> SimilarSlots([FromRoute] string slotType, [FromRoute] int slotId, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -225,8 +216,7 @@ public class SlotsController : ControllerBase
|
|||
[HttpGet("slots/highestRated")]
|
||||
public async Task<IActionResult> HighestRatedSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -248,8 +238,7 @@ public class SlotsController : ControllerBase
|
|||
[HttpGet("slots/tag")]
|
||||
public async Task<IActionResult> SimilarSlots([FromQuery] string tag, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -260,8 +249,8 @@ public class SlotsController : ControllerBase
|
|||
.Select(s => s.SlotId)
|
||||
.ToListAsync();
|
||||
|
||||
IQueryable<Slot> slots = this.database.Slots.ByGameVersion(gameVersion, false, true)
|
||||
.Where(s => slotIdsWithTag.Contains(s.SlotId))
|
||||
IQueryable<Slot> slots = this.database.Slots.Where(s => slotIdsWithTag.Contains(s.SlotId))
|
||||
.ByGameVersion(gameVersion, false, true)
|
||||
.OrderByDescending(s => s.PlaysLBP1)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30));
|
||||
|
@ -276,15 +265,14 @@ public class SlotsController : ControllerBase
|
|||
[HttpGet("slots/mmpicks")]
|
||||
public async Task<IActionResult> TeamPickedSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
GameVersion gameVersion = token.GameVersion;
|
||||
|
||||
IQueryable<Slot> slots = this.database.Slots.ByGameVersion(gameVersion, false, true)
|
||||
.Where(s => s.TeamPick)
|
||||
IQueryable<Slot> slots = this.database.Slots.Where(s => s.TeamPick)
|
||||
.ByGameVersion(gameVersion, false, true)
|
||||
.OrderByDescending(s => s.LastUpdated)
|
||||
.Skip(Math.Max(0, pageStart - 1))
|
||||
.Take(Math.Min(pageSize, 30));
|
||||
|
@ -298,8 +286,7 @@ public class SlotsController : ControllerBase
|
|||
[HttpGet("slots/lbp2luckydip")]
|
||||
public async Task<IActionResult> LuckyDipSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] int seed)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -325,8 +312,7 @@ public class SlotsController : ControllerBase
|
|||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -357,8 +343,7 @@ public class SlotsController : ControllerBase
|
|||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -403,8 +388,7 @@ public class SlotsController : ControllerBase
|
|||
[FromQuery] string? dateFilterType = null
|
||||
)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -435,8 +419,7 @@ public class SlotsController : ControllerBase
|
|||
[FromQuery] bool? move = null
|
||||
)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (pageSize <= 0) return this.BadRequest();
|
||||
|
||||
|
@ -445,7 +428,7 @@ public class SlotsController : ControllerBase
|
|||
foreach (Room room in RoomHelper.Rooms)
|
||||
{
|
||||
// TODO: support developer slotTypes?
|
||||
if(room.Slot.SlotType != SlotType.User) continue;
|
||||
if (room.Slot.SlotType != SlotType.User) continue;
|
||||
|
||||
if (!playersBySlotId.TryGetValue(room.Slot.SlotId, out int playerCount))
|
||||
playersBySlotId.Add(room.Slot.SlotId, 0);
|
||||
|
@ -468,7 +451,7 @@ public class SlotsController : ControllerBase
|
|||
{
|
||||
Slot? slot = await this.database.Slots.ByGameVersion(token.GameVersion, false, true)
|
||||
.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||
if(slot == null) continue; // shouldn't happen ever unless the room is borked
|
||||
if (slot == null) continue; // shouldn't happen ever unless the room is borked
|
||||
|
||||
slots.Add(slot);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/plain")]
|
||||
public class StatisticsController : ControllerBase
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/xml")]
|
||||
public class StoreController : Controller
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
#nullable enable
|
||||
using System.Text.Json;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Files;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Levels;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/xml")]
|
||||
public class UserController : ControllerBase
|
||||
|
@ -34,14 +36,11 @@ public class UserController : ControllerBase
|
|||
{
|
||||
// use an anonymous type to only fetch certain columns
|
||||
var partialUser = await this.database.Users.Where(u => u.Username == username)
|
||||
.Select
|
||||
(
|
||||
u => new
|
||||
{
|
||||
u.Username,
|
||||
u.IconHash,
|
||||
}
|
||||
)
|
||||
.Select(u => new
|
||||
{
|
||||
u.Username,
|
||||
u.IconHash,
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
if (partialUser == null) return null;
|
||||
|
||||
|
@ -52,8 +51,7 @@ public class UserController : ControllerBase
|
|||
[HttpGet("user/{username}")]
|
||||
public async Task<IActionResult> GetUser(string username)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
string? user = await this.getSerializedUser(username, token.GameVersion);
|
||||
if (user == null) return this.NotFound();
|
||||
|
@ -64,9 +62,6 @@ public class UserController : ControllerBase
|
|||
[HttpGet("users")]
|
||||
public async Task<IActionResult> GetUserAlt([FromQuery] string[] u)
|
||||
{
|
||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (token == null) return this.StatusCode(403, "");
|
||||
|
||||
List<string?> serializedUsers = new();
|
||||
foreach (string userId in u) serializedUsers.Add(await this.getSerializedUserPicture(userId));
|
||||
|
||||
|
@ -78,20 +73,12 @@ public class UserController : ControllerBase
|
|||
[HttpPost("updateUser")]
|
||||
public async Task<IActionResult> UpdateUser()
|
||||
{
|
||||
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
||||
GameToken token = this.GetToken();
|
||||
|
||||
if (userAndToken == null) return this.StatusCode(403, "");
|
||||
User? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
User user = userAndToken.Value.Item1;
|
||||
GameToken gameToken = userAndToken.Value.Item2;
|
||||
|
||||
this.Request.Body.Position = 0;
|
||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
// xml hack so we can use one class to deserialize different root names
|
||||
string rootElement = bodyString.Contains("updateUser") ? "updateUser" : "user";
|
||||
XmlSerializer serializer = new(typeof(UserUpdate), new XmlRootAttribute(rootElement));
|
||||
UserUpdate? update = (UserUpdate?)serializer.Deserialize(new StringReader(bodyString));
|
||||
UserUpdate? update = await this.DeserializeBody<UserUpdate>("updateUser", "user");
|
||||
|
||||
if (update == null) return this.BadRequest();
|
||||
|
||||
|
@ -133,7 +120,7 @@ public class UserController : ControllerBase
|
|||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == updateSlot.SlotId);
|
||||
if (slot == null) continue;
|
||||
|
||||
if (slot.CreatorId != gameToken.UserId) continue;
|
||||
if (slot.CreatorId != token.UserId) continue;
|
||||
|
||||
Location? loc = await this.database.Locations.FirstOrDefaultAsync(l => l.Id == slot.LocationId);
|
||||
|
||||
|
@ -146,7 +133,7 @@ public class UserController : ControllerBase
|
|||
|
||||
if (update.PlanetHash != null)
|
||||
{
|
||||
switch (gameToken.GameVersion)
|
||||
switch (token.GameVersion)
|
||||
{
|
||||
case GameVersion.LittleBigPlanet2: // LBP2 planets will apply to LBP3
|
||||
{
|
||||
|
@ -169,7 +156,7 @@ public class UserController : ControllerBase
|
|||
case GameVersion.Unknown:
|
||||
default: // The rest do not support custom earths.
|
||||
{
|
||||
throw new ArgumentException($"invalid gameVersion {gameToken.GameVersion} for setting earth");
|
||||
throw new ArgumentException($"invalid gameVersion {token.GameVersion} for setting earth");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +177,7 @@ public class UserController : ControllerBase
|
|||
[HttpPost("update_my_pins")]
|
||||
public async Task<IActionResult> UpdateMyPins()
|
||||
{
|
||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||
User? user = await this.database.UserFromGameToken(this.GetToken());
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
string pinsString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
|
|
@ -3,6 +3,9 @@ using LBPUnion.ProjectLighthouse.Logging;
|
|||
using LBPUnion.ProjectLighthouse.Middlewares;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Middlewares;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Startup;
|
||||
|
@ -21,6 +24,19 @@ public class GameServerStartup
|
|||
{
|
||||
services.AddControllers();
|
||||
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultScheme = "tokenAuth";
|
||||
options.AddScheme<TokenAuthHandler>("tokenAuth", null);
|
||||
});
|
||||
|
||||
services.AddAuthorization(o =>
|
||||
{
|
||||
AuthorizationPolicyBuilder builder = new("tokenAuth");
|
||||
builder = builder.RequireClaim("userId");
|
||||
o.DefaultPolicy = builder.Build();
|
||||
});
|
||||
|
||||
services.AddMvc
|
||||
(
|
||||
options =>
|
||||
|
@ -64,12 +80,14 @@ public class GameServerStartup
|
|||
app.UseForwardedHeaders();
|
||||
|
||||
app.UseMiddleware<RequestLogMiddleware>();
|
||||
app.UseMiddleware<RateLimitMiddleware>();
|
||||
app.UseMiddleware<DigestMiddleware>(computeDigests);
|
||||
app.UseMiddleware<SetLastContactMiddleware>();
|
||||
app.UseMiddleware<RateLimitMiddleware>();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints => endpoints.MapControllers());
|
||||
app.UseEndpoints(endpoints => endpoints.MapRazorPages());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.GameServer.Startup;
|
||||
|
||||
public class TokenAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||
{
|
||||
private readonly Database database;
|
||||
private const string cookie = "MM_AUTH";
|
||||
|
||||
public TokenAuthHandler
|
||||
(
|
||||
IOptionsMonitor<AuthenticationSchemeOptions> options,
|
||||
UrlEncoder encoder,
|
||||
ISystemClock clock,
|
||||
Database database
|
||||
// I said I don't want any damn vegetables (logs)
|
||||
) : base(options, new NullLoggerFactory(), encoder, clock)
|
||||
{
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
|
||||
{
|
||||
this.Context.Response.StatusCode = 403;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
if (!this.Context.Request.Cookies.ContainsKey(cookie)) return AuthenticateResult.Fail("No auth cookie");
|
||||
|
||||
GameToken? gameToken = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (gameToken == null) return AuthenticateResult.Fail("No game token");
|
||||
|
||||
this.Context.Items["Token"] = gameToken;
|
||||
Claim[] claims = {
|
||||
new("userId", gameToken.UserId.ToString()),
|
||||
};
|
||||
ClaimsIdentity identity = new(claims, this.Scheme.Name);
|
||||
ClaimsPrincipal principal = new(identity);
|
||||
AuthenticationTicket ticket = new(principal, this.Scheme.Name);
|
||||
return AuthenticateResult.Success(ticket);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Administration.Reports;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
|
@ -32,7 +31,7 @@ public class AdminReportController : ControllerBase
|
|||
report.JpegHash,
|
||||
report.GriefStateHash,
|
||||
};
|
||||
if(report.LevelType != "user")
|
||||
if (report.LevelType != "user")
|
||||
hashes.Add(report.InitialStateHash);
|
||||
foreach (string hash in hashes)
|
||||
{
|
||||
|
|
|
@ -27,12 +27,14 @@ public class ModerationSlotController : ControllerBase
|
|||
|
||||
Slot? slot = await this.database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
if (slot == null) return this.NotFound();
|
||||
|
||||
slot.TeamPick = true;
|
||||
|
||||
// Send webhook with slot.Name and slot.Creator.Username
|
||||
await WebhookHelper.SendWebhook("New Team Pick!", $"The level [**{slot.Name}**]({ServerConfiguration.Instance.ExternalUrl}/slot/{slot.SlotId}) by **{slot.Creator?.Username}** has been team picked");
|
||||
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
return this.Redirect("~/slot/" + id);
|
||||
}
|
||||
|
||||
|
@ -44,9 +46,11 @@ public class ModerationSlotController : ControllerBase
|
|||
|
||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
if (slot == null) return this.NotFound();
|
||||
|
||||
slot.TeamPick = false;
|
||||
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
return this.Redirect("~/slot/" + id);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,10 +14,9 @@ public class ResourcesController : ControllerBase
|
|||
string path = FileHelper.GetImagePath($"{hash}.png");
|
||||
|
||||
string fullPath = Path.GetFullPath(path);
|
||||
string basePath = Path.GetFullPath(FileHelper.ImagePath);
|
||||
|
||||
// Prevent directory traversal attacks
|
||||
if (!fullPath.StartsWith(basePath)) return this.BadRequest();
|
||||
if (!fullPath.StartsWith(FileHelper.FullImagePath)) return this.BadRequest();
|
||||
|
||||
if (IOFile.Exists(path)) return this.File(IOFile.OpenRead(path), "image/png");
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
@page "/admin/users"
|
||||
@using LBPUnion.ProjectLighthouse.Administration
|
||||
@using LBPUnion.ProjectLighthouse.PlayerData.Profiles
|
||||
@using LBPUnion.ProjectLighthouse.Types
|
||||
@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Admin.AdminPanelUsersPage
|
||||
|
||||
@{
|
||||
|
@ -61,7 +60,7 @@
|
|||
<select name="role" class="ui selection dropdown">
|
||||
@foreach (PermissionLevel level in Enum.GetValues<PermissionLevel>())
|
||||
{
|
||||
if(level < 0) continue;
|
||||
if (level < 0) continue;
|
||||
string selected = level == user.PermissionLevel ? " selected" : "";
|
||||
|
||||
<option value="@((int)level)"@selected>@level.ToString()</option>
|
||||
|
|
|
@ -63,14 +63,18 @@ public class LoginForm : BaseLayout
|
|||
if (user == null)
|
||||
{
|
||||
Logger.Warn($"User {username} failed to login on web due to invalid username", LogArea.Login);
|
||||
this.Error = "The username or password you entered is invalid.";
|
||||
this.Error = ServerConfiguration.Instance.Mail.MailEnabled
|
||||
? "The email or password you entered is invalid."
|
||||
: "The username or password you entered is invalid.";
|
||||
return this.Page();
|
||||
}
|
||||
|
||||
if (!BCrypt.Net.BCrypt.Verify(password, user.Password))
|
||||
{
|
||||
Logger.Warn($"User {user.Username} (id: {user.UserId}) failed to login on web due to invalid password", LogArea.Login);
|
||||
this.Error = "The username or password you entered is invalid.";
|
||||
this.Error = ServerConfiguration.Instance.Mail.MailEnabled
|
||||
? "The email or password you entered is invalid."
|
||||
: "The username or password you entered is invalid.";
|
||||
return this.Page();
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
// Technically, this should never happen but I'm going to handle it anyways.
|
||||
if (!Model.User.IsModerator)
|
||||
{
|
||||
if(Debugger.IsAttached) Debugger.Break();
|
||||
if (Debugger.IsAttached) Debugger.Break();
|
||||
throw new Exception("Tried to render mod panel with user whose not mod somehow???");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@
|
|||
@if (Model.Creator != null)
|
||||
{
|
||||
string date = "";
|
||||
if(!mini)
|
||||
if (!mini)
|
||||
date = " on " + TimeZoneInfo.ConvertTime(DateTimeOffset.FromUnixTimeMilliseconds(Model.FirstUploaded), timeZoneInfo).DateTime.ToShortDateString();
|
||||
<p>
|
||||
<i>Created by @await Model.Creator.ToLink(Html, ViewData, language) in @Model.GameVersion.ToPrettyString()@date</i>
|
||||
|
|
|
@ -69,7 +69,7 @@ public class SendVerificationEmailPage : BaseLayout
|
|||
}
|
||||
|
||||
string? existingToken = await this.Database.EmailVerificationTokens.Where(v => v.UserId == user.UserId).Select(v => v.EmailToken).FirstOrDefaultAsync();
|
||||
if(existingToken != null)
|
||||
if (existingToken != null)
|
||||
this.Database.EmailVerificationTokens.RemoveWhere(t => t.EmailToken == existingToken);
|
||||
|
||||
EmailVerificationToken verifyToken = new()
|
||||
|
|
|
@ -54,7 +54,7 @@ public class SlotSettingsPage : BaseLayout
|
|||
|
||||
if (this.User == null) return this.Redirect("~/slot/" + slotId);
|
||||
|
||||
if(!this.User.IsModerator && this.User.UserId != this.Slot.CreatorId) return this.Redirect("~/slot/" + slotId);
|
||||
if (!this.User.IsModerator && this.User.UserId != this.Slot.CreatorId) return this.Redirect("~/slot/" + slotId);
|
||||
|
||||
return this.Page();
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ public class UserPage : BaseLayout
|
|||
this.Photos = await this.Database.Photos.Include(p => p.Slot).OrderByDescending(p => p.Timestamp).Where(p => p.CreatorId == userId).Take(6).ToListAsync();
|
||||
|
||||
this.CommentsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled && this.ProfileUser.CommentsEnabled;
|
||||
if(this.CommentsEnabled)
|
||||
if (this.CommentsEnabled)
|
||||
{
|
||||
this.Comments = await this.Database.Comments.Include(p => p.Poster)
|
||||
.OrderByDescending(p => p.Timestamp)
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<script>
|
||||
function onSubmit(e){
|
||||
document.getElementById("avatar-encoded").value = selectedAvatar.toString();
|
||||
@if(ServerConfiguration.Instance.Mail.MailEnabled){
|
||||
@if (ServerConfiguration.Instance.Mail.MailEnabled){
|
||||
<text>
|
||||
let newEmail = document.getElementById("email").value;
|
||||
if (newEmail.length === 0){
|
||||
|
|
|
@ -79,7 +79,7 @@ public class UserSettingsPage : BaseLayout
|
|||
|
||||
if (this.User == null) return this.Redirect("~/user/" + userId);
|
||||
|
||||
if(!this.User.IsModerator && this.User != this.ProfileUser) return this.Redirect("~/user/" + userId);
|
||||
if (!this.User.IsModerator && this.User != this.ProfileUser) return this.Redirect("~/user/" + userId);
|
||||
|
||||
return this.Page();
|
||||
}
|
||||
|
|
|
@ -57,9 +57,6 @@ public class SlotTests : LighthouseServerTest<GameServerTestStartup>
|
|||
|
||||
await database.SaveChangesAsync();
|
||||
|
||||
// XmlSerializer serializer = new(typeof(Slot));
|
||||
// Slot slot = (Slot)serializer.Deserialize(new StringReader(bodyString));
|
||||
|
||||
LoginResult loginResult = await this.Authenticate();
|
||||
|
||||
HttpResponseMessage respMessageA = await this.AuthenticatedRequest
|
||||
|
|
|
@ -89,7 +89,7 @@ public static class MaintenanceHelper
|
|||
exception = e;
|
||||
}
|
||||
|
||||
if(!success)
|
||||
if (!success)
|
||||
{
|
||||
Logger.Error($"Could not run migration {migrationTask.Name()}", LogArea.Database);
|
||||
if (exception != null) Logger.Error(exception.ToDetailedException(), LogArea.Database);
|
||||
|
|
|
@ -31,7 +31,7 @@ public class PerformCaseActionsTask : IRepeatingTask
|
|||
continue;
|
||||
}
|
||||
}
|
||||
else if(@case.Type.AffectsLevel())
|
||||
else if (@case.Type.AffectsLevel())
|
||||
{
|
||||
slot = await @case.GetSlotAsync(database);
|
||||
if (slot == null)
|
||||
|
|
|
@ -54,7 +54,6 @@ public class ServerConfiguration
|
|||
// If a valid YML configuration is available!
|
||||
if (File.Exists(ConfigFileName) && (tempConfig = fromFile(ConfigFileName)) != null)
|
||||
{
|
||||
// Instance = JsonSerializer.Deserialize<ServerConfiguration>(configFile) ?? throw new ArgumentNullException(nameof(ConfigFileName));
|
||||
Instance = tempConfig;
|
||||
|
||||
if (Instance.ConfigVersion < CurrentConfigVersion)
|
||||
|
|
|
@ -175,6 +175,7 @@ public class Database : DbContext
|
|||
comment.ThumbsDown = boo;
|
||||
comment.ThumbsUp = yay;
|
||||
await this.SaveChangesAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -184,13 +185,19 @@ public class Database : DbContext
|
|||
|
||||
if (type == CommentType.Profile)
|
||||
{
|
||||
User? targetUser = await this.Users.FirstOrDefaultAsync(u => u.UserId == targetId);
|
||||
if (targetUser == null) return false;
|
||||
int targetUserId = await this.Users.Where(u => u.UserId == targetId)
|
||||
.Where(u => u.CommentsEnabled)
|
||||
.Select(u => u.UserId)
|
||||
.FirstOrDefaultAsync();
|
||||
if (targetUserId == 0) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Slot? targetSlot = await this.Slots.FirstOrDefaultAsync(u => u.SlotId == targetId);
|
||||
if (targetSlot == null) return false;
|
||||
int targetSlotId = await this.Slots.Where(s => s.SlotId == targetId)
|
||||
.Where(s => s.CommentsEnabled && !s.Hidden)
|
||||
.Select(s => s.SlotId)
|
||||
.FirstOrDefaultAsync();
|
||||
if (targetSlotId == 0) return false;
|
||||
}
|
||||
|
||||
this.Comments.Add
|
||||
|
@ -205,6 +212,7 @@ public class Database : DbContext
|
|||
}
|
||||
);
|
||||
await this.SaveChangesAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -307,6 +315,16 @@ public class Database : DbContext
|
|||
|
||||
#endregion
|
||||
|
||||
#region User Helper Methods
|
||||
|
||||
public async Task<int> UserIdFromUsername(string? username)
|
||||
{
|
||||
if (username == null) return 0;
|
||||
return await this.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Game Token Shenanigans
|
||||
|
||||
public async Task<string> UsernameFromGameToken(GameToken? token)
|
||||
|
@ -316,7 +334,14 @@ public class Database : DbContext
|
|||
return await this.Users.Where(u => u.UserId == token.UserId).Select(u => u.Username).FirstAsync();
|
||||
}
|
||||
|
||||
public async Task<User?> UserFromMMAuth(string authToken, bool allowUnapproved = false)
|
||||
public async Task<User?> UserFromGameToken(GameToken? token)
|
||||
{
|
||||
if (token == null) return null;
|
||||
|
||||
return await this.Users.FirstOrDefaultAsync(u => u.UserId == token.UserId);
|
||||
}
|
||||
|
||||
private async Task<User?> UserFromMMAuth(string authToken, bool allowUnapproved = false)
|
||||
{
|
||||
if (ServerStatics.IsUnitTesting) allowUnapproved = true;
|
||||
GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == authToken);
|
||||
|
@ -324,20 +349,14 @@ public class Database : DbContext
|
|||
if (token == null) return null;
|
||||
if (!allowUnapproved && !token.Approved) return null;
|
||||
|
||||
if (DateTime.Now > token.ExpiresAt)
|
||||
{
|
||||
this.Remove(token);
|
||||
await this.SaveChangesAsync();
|
||||
return null;
|
||||
}
|
||||
if (DateTime.Now <= token.ExpiresAt) return await this.Users.FirstOrDefaultAsync(u => u.UserId == token.UserId);
|
||||
|
||||
return await this.Users.FirstOrDefaultAsync(u => u.UserId == token.UserId);
|
||||
this.Remove(token);
|
||||
await this.SaveChangesAsync();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<User?> UserFromGameToken
|
||||
(GameToken gameToken, bool allowUnapproved = false)
|
||||
=> await this.UserFromMMAuth(gameToken.UserToken, allowUnapproved);
|
||||
|
||||
public async Task<User?> UserFromGameRequest(HttpRequest request, bool allowUnapproved = false)
|
||||
{
|
||||
if (ServerStatics.IsUnitTesting) allowUnapproved = true;
|
||||
|
@ -356,14 +375,12 @@ public class Database : DbContext
|
|||
if (token == null) return null;
|
||||
if (!allowUnapproved && !token.Approved) return null;
|
||||
|
||||
if (DateTime.Now > token.ExpiresAt)
|
||||
{
|
||||
this.Remove(token);
|
||||
await this.SaveChangesAsync();
|
||||
return null;
|
||||
}
|
||||
if (DateTime.Now <= token.ExpiresAt) return token;
|
||||
|
||||
return token;
|
||||
this.Remove(token);
|
||||
await this.SaveChangesAsync();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<(User, GameToken)?> UserAndGameTokenFromRequest(HttpRequest request, bool allowUnapproved = false)
|
||||
|
@ -383,7 +400,6 @@ public class Database : DbContext
|
|||
}
|
||||
|
||||
User? user = await this.Users.FirstOrDefaultAsync(u => u.UserId == token.UserId);
|
||||
|
||||
if (user == null) return null;
|
||||
|
||||
return (user, token);
|
||||
|
@ -400,19 +416,17 @@ public class Database : DbContext
|
|||
return await this.Users.Where(u => u.UserId == token.UserId).Select(u => u.Username).FirstAsync();
|
||||
}
|
||||
|
||||
public User? UserFromLighthouseToken(string lighthouseToken)
|
||||
private User? UserFromLighthouseToken(string lighthouseToken)
|
||||
{
|
||||
WebToken? token = this.WebTokens.FirstOrDefault(t => t.UserToken == lighthouseToken);
|
||||
if (token == null) return null;
|
||||
|
||||
if (DateTime.Now > token.ExpiresAt)
|
||||
{
|
||||
this.Remove(token);
|
||||
this.SaveChanges();
|
||||
return null;
|
||||
}
|
||||
if (DateTime.Now <= token.ExpiresAt) return this.Users.FirstOrDefault(u => u.UserId == token.UserId);
|
||||
|
||||
return this.Users.Include(u => u.Location).FirstOrDefault(u => u.UserId == token.UserId);
|
||||
this.Remove(token);
|
||||
this.SaveChanges();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public User? UserFromWebRequest(HttpRequest request)
|
||||
|
@ -429,48 +443,41 @@ public class Database : DbContext
|
|||
WebToken? token = this.WebTokens.FirstOrDefault(t => t.UserToken == lighthouseToken);
|
||||
if (token == null) return null;
|
||||
|
||||
if (DateTime.Now > token.ExpiresAt)
|
||||
{
|
||||
this.Remove(token);
|
||||
this.SaveChanges();
|
||||
return null;
|
||||
}
|
||||
if (DateTime.Now <= token.ExpiresAt) return token;
|
||||
|
||||
this.Remove(token);
|
||||
this.SaveChanges();
|
||||
|
||||
return null;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
public async Task<User?> UserFromPasswordResetToken(string resetToken)
|
||||
{
|
||||
PasswordResetToken? token = await this.PasswordResetTokens.FirstOrDefaultAsync(token => token.ResetToken == resetToken);
|
||||
if (token == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (token == null) return null;
|
||||
|
||||
if (token.Created < DateTime.Now.AddHours(-1)) // if token is expired
|
||||
{
|
||||
this.PasswordResetTokens.Remove(token);
|
||||
await this.SaveChangesAsync();
|
||||
return null;
|
||||
}
|
||||
if (token.Created >= DateTime.Now.AddHours(-1))
|
||||
return await this.Users.FirstOrDefaultAsync(user => user.UserId == token.UserId);
|
||||
|
||||
return await this.Users.FirstOrDefaultAsync(user => user.UserId == token.UserId);
|
||||
this.PasswordResetTokens.Remove(token);
|
||||
await this.SaveChangesAsync();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsRegistrationTokenValid(string tokenString)
|
||||
{
|
||||
RegistrationToken? token = this.RegistrationTokens.FirstOrDefault(t => t.Token == tokenString);
|
||||
|
||||
if (token == null) return false;
|
||||
|
||||
if (token.Created < DateTime.Now.AddDays(-7)) // if token is expired
|
||||
{
|
||||
this.RegistrationTokens.Remove(token);
|
||||
this.SaveChanges();
|
||||
return false;
|
||||
}
|
||||
if (token.Created >= DateTime.Now.AddDays(-7)) return true;
|
||||
|
||||
this.RegistrationTokens.Remove(token);
|
||||
this.SaveChanges();
|
||||
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task RemoveExpiredTokens()
|
||||
|
@ -478,7 +485,7 @@ public class Database : DbContext
|
|||
foreach (GameToken token in await this.GameTokens.Where(t => DateTime.Now > t.ExpiresAt).ToListAsync())
|
||||
{
|
||||
User? user = await this.Users.FirstOrDefaultAsync(u => u.UserId == token.UserId);
|
||||
if(user != null) user.LastLogout = TimeHelper.TimestampMillis;
|
||||
if (user != null) user.LastLogout = TimeHelper.TimestampMillis;
|
||||
this.GameTokens.Remove(token);
|
||||
}
|
||||
this.WebTokens.RemoveWhere(t => DateTime.Now > t.ExpiresAt);
|
||||
|
@ -491,10 +498,10 @@ public class Database : DbContext
|
|||
public async Task RemoveRegistrationToken(string tokenString)
|
||||
{
|
||||
RegistrationToken? token = await this.RegistrationTokens.FirstOrDefaultAsync(t => t.Token == tokenString);
|
||||
|
||||
if (token == null) return;
|
||||
|
||||
this.RegistrationTokens.Remove(token);
|
||||
|
||||
await this.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
@ -508,6 +515,7 @@ public class Database : DbContext
|
|||
if (user.Username.Length == 0) return; // don't delete the placeholder user
|
||||
|
||||
if (user.Location != null) this.Locations.Remove(user.Location);
|
||||
|
||||
LastContact? lastContact = await this.LastContacts.FirstOrDefaultAsync(l => l.UserId == user.UserId);
|
||||
if (lastContact != null) this.LastContacts.Remove(lastContact);
|
||||
|
||||
|
@ -536,6 +544,7 @@ public class Database : DbContext
|
|||
public async Task RemoveSlot(Slot slot, bool saveChanges = true)
|
||||
{
|
||||
if (slot.Location != null) this.Locations.Remove(slot.Location);
|
||||
|
||||
this.Slots.Remove(slot);
|
||||
|
||||
if (saveChanges) await this.SaveChangesAsync();
|
||||
|
|
58
ProjectLighthouse/Extensions/ControllerExtensions.cs
Normal file
58
ProjectLighthouse/Extensions/ControllerExtensions.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.PlayerData;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Extensions;
|
||||
|
||||
public static class ControllerExtensions
|
||||
{
|
||||
|
||||
public static GameToken GetToken(this ControllerBase controller)
|
||||
{
|
||||
GameToken? token = (GameToken?)(controller.HttpContext.Items["Token"] ?? null);
|
||||
if (token == null) throw new ArgumentNullException($"GameToken was null even though authentication was successful {nameof(controller)}");
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
public static async Task<T?> DeserializeBody<T>(this ControllerBase controller, params string[] rootElements)
|
||||
{
|
||||
controller.Request.Body.Position = 0;
|
||||
string bodyString = await new StreamReader(controller.Request.Body).ReadToEndAsync();
|
||||
|
||||
try
|
||||
{
|
||||
XmlRootAttribute? root = null;
|
||||
if (rootElements.Length > 0)
|
||||
{
|
||||
string? matchedRoot = rootElements.FirstOrDefault(e => bodyString.StartsWith($@"<{e}>"));
|
||||
if (matchedRoot == null)
|
||||
{
|
||||
Logger.Error($"[{controller.ControllerContext.ActionDescriptor.ActionName}] " +
|
||||
$"Failed to deserialize {typeof(T).Name}: Unable to match root element", LogArea.Deserialization);
|
||||
Logger.Error($"{bodyString}", LogArea.Deserialization);
|
||||
return default;
|
||||
}
|
||||
root = new XmlRootAttribute(matchedRoot);
|
||||
}
|
||||
XmlSerializer serializer = new(typeof(T), root);
|
||||
T? obj = (T?)serializer.Deserialize(new StringReader(bodyString));
|
||||
SanitizationHelper.SanitizeStringsInClass(obj);
|
||||
return obj;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error($"[{controller.ControllerContext.ActionDescriptor.ActionName}] " +
|
||||
$"Failed to deserialize {typeof(T).Name}: {e.Message}", LogArea.Deserialization);
|
||||
Logger.Error($"{bodyString}", LogArea.Deserialization);
|
||||
}
|
||||
return default;
|
||||
}
|
||||
}
|
|
@ -24,8 +24,12 @@ public static class FileHelper
|
|||
{
|
||||
public static readonly string ResourcePath = Path.Combine(Environment.CurrentDirectory, "r");
|
||||
|
||||
public static readonly string FullResourcePath = Path.GetFullPath(ResourcePath);
|
||||
|
||||
public static readonly string ImagePath = Path.Combine(Environment.CurrentDirectory, "png");
|
||||
|
||||
public static readonly string FullImagePath = Path.GetFullPath(ImagePath);
|
||||
|
||||
public static string GetResourcePath(string hash) => Path.Combine(ResourcePath, hash);
|
||||
|
||||
public static string GetImagePath(string hash) => Path.Combine(ImagePath, hash);
|
||||
|
@ -99,7 +103,7 @@ public static class FileHelper
|
|||
|
||||
int curOffset = 8;
|
||||
int dependencyTableOffset = BinaryPrimitives.ReadInt32BigEndian(file.Data.AsSpan()[curOffset..]);
|
||||
if(dependencyTableOffset <= 0 || dependencyTableOffset > file.Data.Length) return dependencies;
|
||||
if (dependencyTableOffset <= 0 || dependencyTableOffset > file.Data.Length) return dependencies;
|
||||
|
||||
curOffset = dependencyTableOffset;
|
||||
int dependencyTableSize = BinaryPrimitives.ReadInt32BigEndian(file.Data.AsSpan()[dependencyTableOffset..]);
|
||||
|
@ -163,7 +167,7 @@ public static class FileHelper
|
|||
{
|
||||
version = GameVersion.LittleBigPlanet3;
|
||||
}
|
||||
else if(revision <= lbp2Latest)
|
||||
else if (revision <= lbp2Latest)
|
||||
{
|
||||
version = GameVersion.LittleBigPlanet2;
|
||||
}
|
||||
|
@ -404,7 +408,7 @@ public static class FileHelper
|
|||
private static bool DDSToPNG(string hash, byte[] data)
|
||||
{
|
||||
Dds ddsImage = Dds.Create(data, new PfimConfig());
|
||||
if(ddsImage.Compressed)
|
||||
if (ddsImage.Compressed)
|
||||
ddsImage.Decompress();
|
||||
|
||||
// ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault
|
||||
|
|
|
@ -45,7 +45,7 @@ public static class CryptoHelper
|
|||
using IncrementalHash sha1 = IncrementalHash.CreateHash(HashAlgorithmName.SHA1);
|
||||
// LBP games will sometimes opt to calculate the digest without the body
|
||||
// (one example is resource upload requests)
|
||||
if(!excludeBody)
|
||||
if (!excludeBody)
|
||||
sha1.AppendData(bodyBytes);
|
||||
if (cookieBytes.Length > 0) sha1.AppendData(cookieBytes);
|
||||
sha1.AppendData(pathBytes);
|
||||
|
|
|
@ -25,4 +25,5 @@ public enum LogArea
|
|||
Maintenance,
|
||||
Score,
|
||||
RateLimit,
|
||||
Deserialization,
|
||||
}
|
|
@ -154,23 +154,19 @@ public class User
|
|||
public UserStatus Status => new(this.database, this.UserId);
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsBanned => this.PermissionLevel == PermissionLevel.Banned;
|
||||
public bool IsBanned => this.PermissionLevel is PermissionLevel.Banned;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsRestricted => this.PermissionLevel == PermissionLevel.Restricted ||
|
||||
this.PermissionLevel == PermissionLevel.Banned;
|
||||
public bool IsRestricted => this.PermissionLevel is PermissionLevel.Restricted or PermissionLevel.Banned;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsSilenced => this.PermissionLevel == PermissionLevel.Silenced ||
|
||||
this.PermissionLevel == PermissionLevel.Restricted ||
|
||||
this.PermissionLevel == PermissionLevel.Banned;
|
||||
public bool IsSilenced => this.PermissionLevel is PermissionLevel.Silenced or PermissionLevel.Restricted or PermissionLevel.Banned;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsModerator => this.PermissionLevel == PermissionLevel.Moderator ||
|
||||
this.PermissionLevel == PermissionLevel.Administrator;
|
||||
public bool IsModerator => this.PermissionLevel is PermissionLevel.Moderator or PermissionLevel.Administrator;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsAdmin => this.PermissionLevel == PermissionLevel.Administrator;
|
||||
public bool IsAdmin => this.PermissionLevel is PermissionLevel.Administrator;
|
||||
|
||||
[JsonIgnore]
|
||||
public PermissionLevel PermissionLevel { get; set; } = PermissionLevel.Default;
|
||||
|
|
|
@ -54,7 +54,7 @@ public static class StartupTasks
|
|||
using Database database = new();
|
||||
|
||||
#if !DEBUG
|
||||
if(serverType == ServerType.GameServer)
|
||||
if (serverType == ServerType.GameServer)
|
||||
#endif
|
||||
migrateDatabase(database);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue