From e0010e1d31633795591df031a702991f49d06e26 Mon Sep 17 00:00:00 2001 From: AeonLucid Date: Thu, 4 Nov 2021 06:29:58 +0100 Subject: [PATCH] Now able to enter the lobby --- .../Controllers/ClientController.cs | 31 +- .../Controllers/CloudScriptController.cs | 281 +++++++++++++++++- .../Converters/DateTimeConverter.cs | 24 ++ .../Middleware/RequestLoggerMiddleware.cs | 9 +- .../Models/Client/FUpdateUserDataRequest.cs | 38 +++ .../Models/Client/FUpdateUserDataResult.cs | 11 + .../Data/FYActiveContractPlayerData.cs | 14 + .../CloudScript/Data/FYFactionContractData.cs | 13 + .../Data/FYFactionContractsData.cs | 14 + .../Data/FYFactionsContractsData.cs | 14 + .../Data/FYItemCurrentlyBeingCrafted.cs | 19 ++ .../Models/CloudScript/Data/FYTimestamp.cs | 10 + .../FYGetCraftingInProgressDataResult.cs | 17 ++ .../CloudScript/FYGetPlayerContractsResult.cs | 24 ++ .../Services/Auth/AuthTokenService.cs | 7 +- .../Services/Database/DbUserDataService.cs | 9 + .../Services/UserData/UserDataService.cs | 61 ++++ src/Prospect.Server.Api/Startup.cs | 6 +- src/Prospect.sln.DotSettings | 1 + 19 files changed, 593 insertions(+), 10 deletions(-) create mode 100644 src/Prospect.Server.Api/Converters/DateTimeConverter.cs create mode 100644 src/Prospect.Server.Api/Models/Client/FUpdateUserDataRequest.cs create mode 100644 src/Prospect.Server.Api/Models/Client/FUpdateUserDataResult.cs create mode 100644 src/Prospect.Server.Api/Models/CloudScript/Data/FYActiveContractPlayerData.cs create mode 100644 src/Prospect.Server.Api/Models/CloudScript/Data/FYFactionContractData.cs create mode 100644 src/Prospect.Server.Api/Models/CloudScript/Data/FYFactionContractsData.cs create mode 100644 src/Prospect.Server.Api/Models/CloudScript/Data/FYFactionsContractsData.cs create mode 100644 src/Prospect.Server.Api/Models/CloudScript/Data/FYItemCurrentlyBeingCrafted.cs create mode 100644 src/Prospect.Server.Api/Models/CloudScript/Data/FYTimestamp.cs create mode 100644 src/Prospect.Server.Api/Models/CloudScript/FYGetCraftingInProgressDataResult.cs create mode 100644 src/Prospect.Server.Api/Models/CloudScript/FYGetPlayerContractsResult.cs diff --git a/src/Prospect.Server.Api/Controllers/ClientController.cs b/src/Prospect.Server.Api/Controllers/ClientController.cs index ab1d208..16fa189 100644 --- a/src/Prospect.Server.Api/Controllers/ClientController.cs +++ b/src/Prospect.Server.Api/Controllers/ClientController.cs @@ -19,7 +19,6 @@ namespace Prospect.Server.Api.Controllers { [ApiController] [Route("Client")] - [Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)] public class ClientController : Controller { private readonly PlayFabSettings _settings; @@ -127,17 +126,20 @@ namespace Prospect.Server.Api.Controllers [HttpPost("AddGenericID")] [Produces("application/json")] + [Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)] public IActionResult AddGenericId(FAddGenericIDRequest request) { - return Ok(new ClientResponse + return Ok(new ClientResponse { Code = 200, - Status = "OK" + Status = "OK", + Data = new object() }); } [HttpPost("UpdateUserTitleDisplayName")] [Produces("application/json")] + [Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)] public IActionResult UpdateUserTitleDisplayName(FUpdateUserTitleDisplayNameRequest request) { return Ok(new ClientResponse @@ -151,8 +153,28 @@ namespace Prospect.Server.Api.Controllers }); } + [HttpPost("UpdateUserData")] + [Produces("application/json")] + [Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)] + public async Task UpdateUserData(FUpdateUserDataRequest request) + { + var userId = User.FindAuthUserId(); + await _userDataService.UpdateAsync(userId, request.PlayFabId, request.Data); + + return Ok(new ClientResponse + { + Code = 200, + Status = "OK", + Data = new FUpdateUserDataResult + { + DataVersion = 0 + } + }); + } + [HttpPost("GetUserData")] [Produces("application/json")] + [Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)] public async Task GetUserData(FGetUserDataRequest request) { var userId = User.FindAuthUserId(); @@ -172,6 +194,7 @@ namespace Prospect.Server.Api.Controllers [HttpPost("GetUserReadOnlyData")] [Produces("application/json")] + [Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)] public IActionResult GetUserReadOnlyData(FGetUserDataRequest request) { return Ok(new ClientResponse @@ -188,6 +211,7 @@ namespace Prospect.Server.Api.Controllers [HttpPost("GetUserInventory")] [Produces("application/json")] + [Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)] public IActionResult GetUserReadOnlyData(FGetUserInventoryRequest request) { return Ok(new ClientResponse @@ -229,6 +253,7 @@ namespace Prospect.Server.Api.Controllers [HttpPost("GetTitleData")] [Produces("application/json")] + [Authorize(AuthenticationSchemes = UserAuthenticationOptions.DefaultScheme)] public IActionResult GetTitleData(FGetTitleDataRequest request) { return Ok(new ClientResponse diff --git a/src/Prospect.Server.Api/Controllers/CloudScriptController.cs b/src/Prospect.Server.Api/Controllers/CloudScriptController.cs index 27c3e84..9909a34 100644 --- a/src/Prospect.Server.Api/Controllers/CloudScriptController.cs +++ b/src/Prospect.Server.Api/Controllers/CloudScriptController.cs @@ -1,7 +1,13 @@ -using Microsoft.AspNetCore.Authorization; +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Prospect.Server.Api.Models.Client; +using Prospect.Server.Api.Models.CloudScript; +using Prospect.Server.Api.Models.CloudScript.Data; using Prospect.Server.Api.Services.Auth.Entity; +using Prospect.Server.Api.Services.Auth.Extensions; namespace Prospect.Server.Api.Controllers { @@ -10,10 +16,280 @@ namespace Prospect.Server.Api.Controllers [Authorize(AuthenticationSchemes = EntityAuthenticationOptions.DefaultScheme)] public class CloudScriptController : Controller { + private readonly ILogger _logger; + + public CloudScriptController(ILogger logger) + { + _logger = logger; + } + [HttpPost("ExecuteFunction")] [Produces("application/json")] public IActionResult ExecuteFunction(FExecuteFunctionRequest request) { + _logger.LogInformation("Executing function {Function}", request.FunctionName); + + object result = null; + + switch (request.FunctionName) + { + case "RequestMaintenanceModeState": + result = new + { + enabled = false + }; + break; + case "GetCharacterVanity": + result = new + { + userId = User.FindAuthUserId(), + error = (object) null, + returnVanity = new + { + userId = User.FindAuthUserId(), + head_item = new + { + id = "Black02M_Head1", + materialIndex = 0, + }, + boots_item = new + { + id = "StarterOutfit01_Boots_M", + materialIndex = 0, + }, + chest_item = new + { + id = "StarterOutfit01_Chest_M", + materialIndex = 0, + }, + glove_item = new + { + id = "StarterOutfit01_Gloves_M", + materialIndex = 0, + }, + base_suit_item = new + { + id = "StarterOutfit01M_BaseSuit", + materialIndex = 0, + }, + melee_weapon_item = new + { + id = "Melee_Omega", + materialIndex = 0, + }, + body_type = 1, + archetype_id = "TheProspector", + slot_index = 0 + } + }; + break; + case "SetMatchAllowJoin": + result = new + { + sessionId = (object)null, + success = false + }; + break; + case "GetFriendList": + // TODO: ? + result = new + { + + }; + break; + case "GetPlayerSets": + result = new + { + success = true, + entries = new [] + { + new + { + setData = new + { + id = "", + userId = User.FindAuthUserId(), + kit = "", + shield = "", + helmet = "", + weaponOne = "", + weaponTwo = "", + bag = "", + bagItemsAsJsonStr = "", + safeItemsAsJsonStr = "" + }, + items = Array.Empty() + } + } + }; + break; + case "RequestFactionProgression": + result = new + { + factions = new [] + { + new + { + factionId = "ICA", + currentProgression = 0 + }, + new + { + factionId = "Korolev", + currentProgression = 0 + }, + new + { + factionId = "Osiris", + currentProgression = 0 + }, + }, + userId = User.FindAuthUserId(), + error = "" + }; + break; + case "GetPlayersInventoriesLimits": + result = new + { + success = true, + entries = new [] + { + new { + userId = User.FindAuthUserId(), + inventoryStashLimit = 75, + inventoryBagLimit = 300, + inventorySafeLimit = 5 + } + } + }; + break; + case "UpdateRetentionBonus": + result = new + { + playerData = new + { + daysClaimed = 0, + lastClaimTime = new + { + seconds = 1635033600 + } + }, + userId = User.FindAuthUserId(), + error = (object)null + }; + break; + case "GetCraftingInProgressData": + result = new FYGetCraftingInProgressDataResult + { + UserId = User.FindAuthUserId(), + Error = null, + ItemCurrentlyBeingCrafted = new FYItemCurrentlyBeingCrafted + { + ItemId = null, + ItemRarity = -1, + PurchaseAmount = -1, + UtcTimestampWhenCraftingStarted = new FYTimestamp + { + Seconds = 0 + } + } + }; + break; + case "RequestPlayerContracts": + result = new FYGetPlayerContractsResult + { + UserId = User.FindAuthUserId(), + Error = null, + ActiveContracts = new List(), + FactionsContracts = new FYFactionsContractsData + { + Boards = new List + { + new FYFactionContractsData + { + FactionId = "ICA", + Contracts = new List + { + new FYFactionContractData + { + ContractId = "NEW-Easy-ICA-Gather-1", + ContractIsLockedDueToLowFactionReputation = false + }, + new FYFactionContractData + { + ContractId = "NEW-Medium-ICA-Uplink-1", + ContractIsLockedDueToLowFactionReputation = false + }, + new FYFactionContractData + { + ContractId = "NEW-Hard-ICA-Uplink-1", + ContractIsLockedDueToLowFactionReputation = false + } + } + }, + new FYFactionContractsData + { + FactionId = "Korolev", + Contracts = new List + { + new FYFactionContractData + { + ContractId = "NEW-Easy-KOR-Mine-4", + ContractIsLockedDueToLowFactionReputation = false + }, + new FYFactionContractData + { + ContractId = "NEW-Medium-KOR-Mine-1", + ContractIsLockedDueToLowFactionReputation = false + }, + new FYFactionContractData + { + ContractId = "NEW-Hard-KOR-PvP-6", + ContractIsLockedDueToLowFactionReputation = false + } + } + }, + new FYFactionContractsData + { + FactionId = "Osiris", + Contracts = new List + { + new FYFactionContractData + { + ContractId = "NEW-Easy-Osiris-Brightcaps-1", + ContractIsLockedDueToLowFactionReputation = false + }, + new FYFactionContractData + { + ContractId = "NEW-Medium-Osiris-Gather-1", + ContractIsLockedDueToLowFactionReputation = false + }, + new FYFactionContractData + { + ContractId = "NEW-Hard-Osiris-Gather-7", + ContractIsLockedDueToLowFactionReputation = false + } + } + } + }, + LastBoardRefreshTimeUtc = new FYTimestamp + { + Seconds = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() + } + }, + RefreshHours24UtcFromBackend = 12 + }; + break; + default: + _logger.LogWarning("Missing function {Function}", request.FunctionName); + + result = new + { + + }; + break; + } + return Ok(new ClientResponse { Code = 200, @@ -21,7 +297,8 @@ namespace Prospect.Server.Api.Controllers Data = new FExecuteFunctionResult { ExecutionTimeMilliseconds = 12, - FunctionName = request.FunctionName + FunctionName = request.FunctionName, + FunctionResult = result } }); } diff --git a/src/Prospect.Server.Api/Converters/DateTimeConverter.cs b/src/Prospect.Server.Api/Converters/DateTimeConverter.cs new file mode 100644 index 0000000..1ea4e79 --- /dev/null +++ b/src/Prospect.Server.Api/Converters/DateTimeConverter.cs @@ -0,0 +1,24 @@ +using System; +using System.Diagnostics; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Prospect.Server.Api.Converters +{ + // By default System.Text.Json outputs and we want: + // Output: 2021-11-04T04:58:20.4232184Z + // Want : 2021-10-24T02:54:56.652Z + public class DateTimeConverter : JsonConverter + { + public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + Debug.Assert(typeToConvert == typeof(DateTime)); + return DateTime.Parse(reader.GetString()); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK")); + } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Middleware/RequestLoggerMiddleware.cs b/src/Prospect.Server.Api/Middleware/RequestLoggerMiddleware.cs index fd91bca..eee8f06 100644 --- a/src/Prospect.Server.Api/Middleware/RequestLoggerMiddleware.cs +++ b/src/Prospect.Server.Api/Middleware/RequestLoggerMiddleware.cs @@ -25,7 +25,14 @@ namespace Prospect.Server.Api.Middleware if (context.Request.Method == "POST") { - _logger.LogDebug("URL {Url} Body {Body}", context.Request.GetDisplayUrl(), body); + var requestId = "NULL"; + + if (context.Request.Headers.TryGetValue("X-RequestID", out var requestIdValues)) + { + requestId = requestIdValues.ToString(); + } + + _logger.LogDebug("URL {Url} RequestId {RequestId} Body {Body}", context.Request.GetDisplayUrl(), requestId, body); } await _next(context); diff --git a/src/Prospect.Server.Api/Models/Client/FUpdateUserDataRequest.cs b/src/Prospect.Server.Api/Models/Client/FUpdateUserDataRequest.cs new file mode 100644 index 0000000..06732d6 --- /dev/null +++ b/src/Prospect.Server.Api/Models/Client/FUpdateUserDataRequest.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Prospect.Server.Api.Models.Client.Data; + +namespace Prospect.Server.Api.Models.Client +{ + public class FUpdateUserDataRequest + { + /// + /// [optional] The optional custom tags associated with the request (e.g. build number, external trace identifiers, etc.). + /// + [JsonPropertyName("CustomTags")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public Dictionary CustomTags { get; set; } + + /// + /// [optional] Key-value pairs to be written to the custom data. Note that keys are trimmed of whitespace, are limited in size, and may + /// not begin with a '!' character or be null. + /// + public Dictionary Data { get; set; } + + /// + /// [optional] Optional list of Data-keys to remove from UserData. Some SDKs cannot insert null-values into Data due to language + /// constraints. Use this to delete the keys directly. + /// + public List KeysToRemove { get; set; } + + /// + /// [optional] Permission to be applied to all user data keys written in this request. Defaults to "private" if not set. + /// + public UserDataPermission? Permission { get; set; } + + /// + /// Unique PlayFab assigned ID of the user on whom the operation will be performed. + /// + public string PlayFabId { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Models/Client/FUpdateUserDataResult.cs b/src/Prospect.Server.Api/Models/Client/FUpdateUserDataResult.cs new file mode 100644 index 0000000..18299a2 --- /dev/null +++ b/src/Prospect.Server.Api/Models/Client/FUpdateUserDataResult.cs @@ -0,0 +1,11 @@ +namespace Prospect.Server.Api.Models.Client +{ + public class FUpdateUserDataResult + { + /// + /// Indicates the current version of the data that has been set. This is incremented with every set call for that type of + /// data (read-only, internal, etc). This version can be provided in Get calls to find updated data. + /// + public uint DataVersion { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Models/CloudScript/Data/FYActiveContractPlayerData.cs b/src/Prospect.Server.Api/Models/CloudScript/Data/FYActiveContractPlayerData.cs new file mode 100644 index 0000000..02a21c8 --- /dev/null +++ b/src/Prospect.Server.Api/Models/CloudScript/Data/FYActiveContractPlayerData.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Prospect.Server.Api.Models.CloudScript.Data +{ + public class FYActiveContractPlayerData + { + [JsonPropertyName("contractId")] + public string ContractId { get; set; } + + [JsonPropertyName("progress")] + public List Progress { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Models/CloudScript/Data/FYFactionContractData.cs b/src/Prospect.Server.Api/Models/CloudScript/Data/FYFactionContractData.cs new file mode 100644 index 0000000..ea35970 --- /dev/null +++ b/src/Prospect.Server.Api/Models/CloudScript/Data/FYFactionContractData.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace Prospect.Server.Api.Models.CloudScript.Data +{ + public class FYFactionContractData + { + [JsonPropertyName("contractId")] + public string ContractId { get; set; } + + [JsonPropertyName("contractIsLockedDueToLowFactionReputation")] + public bool ContractIsLockedDueToLowFactionReputation { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Models/CloudScript/Data/FYFactionContractsData.cs b/src/Prospect.Server.Api/Models/CloudScript/Data/FYFactionContractsData.cs new file mode 100644 index 0000000..c4a3171 --- /dev/null +++ b/src/Prospect.Server.Api/Models/CloudScript/Data/FYFactionContractsData.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Prospect.Server.Api.Models.CloudScript.Data +{ + public class FYFactionContractsData + { + [JsonPropertyName("factionId")] + public string FactionId { get; set; } + + [JsonPropertyName("contracts")] + public List Contracts { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Models/CloudScript/Data/FYFactionsContractsData.cs b/src/Prospect.Server.Api/Models/CloudScript/Data/FYFactionsContractsData.cs new file mode 100644 index 0000000..ef9b091 --- /dev/null +++ b/src/Prospect.Server.Api/Models/CloudScript/Data/FYFactionsContractsData.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Prospect.Server.Api.Models.CloudScript.Data +{ + public class FYFactionsContractsData + { + [JsonPropertyName("boards")] + public List Boards { get; set; } + + [JsonPropertyName("lastBoardRefreshTimeUtc")] + public FYTimestamp LastBoardRefreshTimeUtc { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Models/CloudScript/Data/FYItemCurrentlyBeingCrafted.cs b/src/Prospect.Server.Api/Models/CloudScript/Data/FYItemCurrentlyBeingCrafted.cs new file mode 100644 index 0000000..d455e17 --- /dev/null +++ b/src/Prospect.Server.Api/Models/CloudScript/Data/FYItemCurrentlyBeingCrafted.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; + +namespace Prospect.Server.Api.Models.CloudScript.Data +{ + public class FYItemCurrentlyBeingCrafted + { + [JsonPropertyName("")] + public string ItemId { get; set; } + + [JsonPropertyName("")] + public int ItemRarity { get; set; } + + [JsonPropertyName("")] + public int PurchaseAmount { get; set; } + + [JsonPropertyName("")] + public FYTimestamp UtcTimestampWhenCraftingStarted { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Models/CloudScript/Data/FYTimestamp.cs b/src/Prospect.Server.Api/Models/CloudScript/Data/FYTimestamp.cs new file mode 100644 index 0000000..3bbfa7d --- /dev/null +++ b/src/Prospect.Server.Api/Models/CloudScript/Data/FYTimestamp.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Prospect.Server.Api.Models.CloudScript.Data +{ + public class FYTimestamp + { + [JsonPropertyName("seconds")] + public int Seconds { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Models/CloudScript/FYGetCraftingInProgressDataResult.cs b/src/Prospect.Server.Api/Models/CloudScript/FYGetCraftingInProgressDataResult.cs new file mode 100644 index 0000000..7d2a489 --- /dev/null +++ b/src/Prospect.Server.Api/Models/CloudScript/FYGetCraftingInProgressDataResult.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; +using Prospect.Server.Api.Models.CloudScript.Data; + +namespace Prospect.Server.Api.Models.CloudScript +{ + public class FYGetCraftingInProgressDataResult + { + [JsonPropertyName("userId")] + public string UserId { get; set; } + + [JsonPropertyName("error")] + public string Error { get; set; } + + [JsonPropertyName("itemCurrentlyBeingCrafted")] + public FYItemCurrentlyBeingCrafted ItemCurrentlyBeingCrafted { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Models/CloudScript/FYGetPlayerContractsResult.cs b/src/Prospect.Server.Api/Models/CloudScript/FYGetPlayerContractsResult.cs new file mode 100644 index 0000000..a85a691 --- /dev/null +++ b/src/Prospect.Server.Api/Models/CloudScript/FYGetPlayerContractsResult.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Prospect.Server.Api.Models.CloudScript.Data; + +namespace Prospect.Server.Api.Models.CloudScript +{ + public class FYGetPlayerContractsResult + { + [JsonPropertyName("userId")] + public string UserId { get; set; } + + [JsonPropertyName("error")] + public string Error { get; set; } + + [JsonPropertyName("activeContracts")] + public List ActiveContracts { get; set; } + + [JsonPropertyName("factionsContracts")] + public FYFactionsContractsData FactionsContracts { get; set; } + + [JsonPropertyName("refreshHours24UtcFromBackend")] + public int RefreshHours24UtcFromBackend { get; set; } + } +} \ No newline at end of file diff --git a/src/Prospect.Server.Api/Services/Auth/AuthTokenService.cs b/src/Prospect.Server.Api/Services/Auth/AuthTokenService.cs index ffc0592..ff5986e 100644 --- a/src/Prospect.Server.Api/Services/Auth/AuthTokenService.cs +++ b/src/Prospect.Server.Api/Services/Auth/AuthTokenService.cs @@ -38,12 +38,12 @@ namespace Prospect.Server.Api.Services.Auth return _tokenHandler.WriteToken(token); } - public string GenerateUser(PlayFabEntity user) + public string GenerateUser(PlayFabEntity entity) { return CreateToken(new[] { - new Claim(AuthClaimTypes.UserId, user.UserId), - new Claim(AuthClaimTypes.EntityId, user.Id), + new Claim(AuthClaimTypes.UserId, entity.UserId), + new Claim(AuthClaimTypes.EntityId, entity.Id), new Claim(AuthClaimTypes.Type, AuthType.User), }); } @@ -52,6 +52,7 @@ namespace Prospect.Server.Api.Services.Auth { return CreateToken(new[] { + new Claim(AuthClaimTypes.UserId, entity.UserId), new Claim(AuthClaimTypes.EntityId, entity.Id), new Claim(AuthClaimTypes.Type, AuthType.Entity), }); diff --git a/src/Prospect.Server.Api/Services/Database/DbUserDataService.cs b/src/Prospect.Server.Api/Services/Database/DbUserDataService.cs index dd88f48..2e78429 100644 --- a/src/Prospect.Server.Api/Services/Database/DbUserDataService.cs +++ b/src/Prospect.Server.Api/Services/Database/DbUserDataService.cs @@ -47,5 +47,14 @@ namespace Prospect.Server.Api.Services.Database return await query.ToCursorAsync(); } + + public async Task UpdateValueAsync(string dataId, string value) + { + var update = Builders.Update + .Set(data => data.Value, value) + .Set(data => data.LastUpdated, DateTime.UtcNow); + + await Collection.UpdateOneAsync(data => data.Id == dataId, update); + } } } \ No newline at end of file diff --git a/src/Prospect.Server.Api/Services/UserData/UserDataService.cs b/src/Prospect.Server.Api/Services/UserData/UserDataService.cs index 6f12e24..716eb2e 100644 --- a/src/Prospect.Server.Api/Services/UserData/UserDataService.cs +++ b/src/Prospect.Server.Api/Services/UserData/UserDataService.cs @@ -117,5 +117,66 @@ namespace Prospect.Server.Api.Services.UserData return result; } + + /// + /// + /// + /// + /// The authenticated user id. + /// + /// + /// The requested user id. + /// + /// + /// + public async Task UpdateAsync( + string currentUserId, + string requestUserId, + Dictionary changes) + { + if (changes.Count == 0) + { + return; + } + + if (string.IsNullOrEmpty(currentUserId)) + { + throw new ArgumentNullException(nameof(currentUserId)); + } + + if (requestUserId == null) + { + requestUserId = currentUserId; + } + + // Whether we are updating someone else. + var other = currentUserId != requestUserId; + + foreach (var (key, value) in changes) + { + var data = await _dbUserDataService.FindAsync(currentUserId, key); + if (data == null) + { + if (other) + { + _logger.LogWarning("User {PlayFabId} attempted to update non-existing key {Key} of another user {PlayFabIdOther}", currentUserId, key, requestUserId); + } + else + { + await _dbUserDataService.InsertAsync(currentUserId, key, value, false); + } + + continue; + } + + if (!data.Public && other) + { + _logger.LogWarning("User {PlayFabId} attempted to update non-public key {Key} of another user {PlayFabIdOther}", currentUserId, key, data.PlayFabId); + continue; + } + + await _dbUserDataService.UpdateValueAsync(data.Id, value); + } + } } } \ No newline at end of file diff --git a/src/Prospect.Server.Api/Startup.cs b/src/Prospect.Server.Api/Startup.cs index a64cb37..05da2ad 100644 --- a/src/Prospect.Server.Api/Startup.cs +++ b/src/Prospect.Server.Api/Startup.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Prospect.Server.Api.Config; +using Prospect.Server.Api.Converters; using Prospect.Server.Api.Middleware; using Prospect.Server.Api.Services.Auth; using Prospect.Server.Api.Services.Auth.User; @@ -44,7 +45,10 @@ namespace Prospect.Server.Api .AddUserAuthentication(_ => {}) .AddEntityAuthentication(_ => {}); - services.AddControllers(); + services.AddControllers().AddJsonOptions(options => + { + options.JsonSerializerOptions.Converters.Add(new DateTimeConverter()); + }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) diff --git a/src/Prospect.sln.DotSettings b/src/Prospect.sln.DotSettings index a0c00fe..e4160b9 100644 --- a/src/Prospect.sln.DotSettings +++ b/src/Prospect.sln.DotSettings @@ -4,4 +4,5 @@ <NamingElement Priority="1"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="__interface" /><type Name="class" /><type Name="struct" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement> <NamingElement Priority="10"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="member function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement> <NamingElement Priority="17"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="namespace" /><type Name="namespace alias" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement> + FY PF \ No newline at end of file