From 2a4edfffef70312d7e50f03e38c1288985838e9a Mon Sep 17 00:00:00 2001 From: Spirtix Date: Mon, 19 Jun 2023 19:59:42 +0200 Subject: [PATCH] Get/Set pairs --- mitm-redirect.py | 2 +- src/Controllers/Common/ContentController.cs | 74 ++++++++++++++++++- src/Model/DBContext.cs | 13 ++++ src/Model/Pair.cs | 18 +++++ src/Model/PairData.cs | 21 ++++++ src/Program.cs | 3 + src/Schema/Pair.cs | 16 +++++ src/Schema/PairData.cs | 12 ++++ src/Services/KeyValueService.cs | 79 +++++++++++++++++++++ 9 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 src/Model/Pair.cs create mode 100644 src/Model/PairData.cs create mode 100644 src/Schema/Pair.cs create mode 100644 src/Schema/PairData.cs create mode 100644 src/Services/KeyValueService.cs diff --git a/mitm-redirect.py b/mitm-redirect.py index ec99e3f..c2d5f37 100644 --- a/mitm-redirect.py +++ b/mitm-redirect.py @@ -3,7 +3,7 @@ import mitmproxy.http def routable(path): - methods = ['GetRules', 'LoginParent', 'RegisterParent', 'GetSubscriptionInfo', 'GetUserInfoByApiToken', 'IsValidApiToken_V2', 'ValidateName', 'GetDefaultNameSuggestion', 'RegisterChild', 'GetProfileByUserId', 'LoginChild', 'GetUserProfileByUserID'] + methods = ['GetRules', 'LoginParent', 'RegisterParent', 'GetSubscriptionInfo', 'GetUserInfoByApiToken', 'IsValidApiToken_V2', 'ValidateName', 'GetDefaultNameSuggestion', 'RegisterChild', 'GetProfileByUserId', 'LoginChild', 'GetUserProfileByUserID', 'GetKeyValuePair', 'SetKeyValuePair'] for method in methods: if method in path: return True diff --git a/src/Controllers/Common/ContentController.cs b/src/Controllers/Common/ContentController.cs index c9b31a7..ef3054e 100644 --- a/src/Controllers/Common/ContentController.cs +++ b/src/Controllers/Common/ContentController.cs @@ -1,15 +1,19 @@ using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; using sodoff.Attributes; using sodoff.Model; using sodoff.Schema; +using sodoff.Services; using sodoff.Util; namespace sodoff.Controllers.Common; public class ContentController : Controller { private readonly DBContext ctx; - public ContentController(DBContext ctx) { + private KeyValueService keyValueService; + public ContentController(DBContext ctx, KeyValueService keyValueService) { this.ctx = ctx; + this.keyValueService = keyValueService; } [HttpPost] @@ -50,4 +54,72 @@ public class ContentController : Controller { return Ok(); } } + + [HttpPost] + [Produces("application/xml")] + [Route("ContentWebService.asmx/GetKeyValuePair")] + public Schema.PairData? GetKeyValuePair([FromForm] string apiToken, [FromForm] int pairId) { + // Find session by apiToken + Session? session = ctx.Sessions.FirstOrDefault(s => s.ApiToken == apiToken); + if (session is null) + return null; + + // Get the pair + Model.PairData? pair = keyValueService.GetPairData(session, pairId); + + return keyValueService.ModelToSchema(pair); + } + + [HttpPost] + [Produces("application/xml")] + [Route("ContentWebService.asmx/SetKeyValuePair")] + public IActionResult SetKeyValuePair([FromForm] string apiToken, [FromForm] int pairId, [FromForm] string contentXML) { + Schema.PairData schemaData = XmlUtil.DeserializeXml(contentXML); + + // Get the session + Session? session = ctx.Sessions.FirstOrDefault(s => s.ApiToken == apiToken); + if (session is null) + return Ok(false); + + bool result = keyValueService.SetPairData(session, pairId, schemaData); + + return Ok(result); + } + + [HttpPost] + [Produces("application/xml")] + [Route("ContentWebService.asmx/GetKeyValuePairByUserID")] + public Schema.PairData? GetKeyValuePairByUserID([FromForm] string apiToken, [FromForm] int pairId, [FromForm] string userId) { + Session? session = ctx.Sessions.FirstOrDefault(s => s.ApiToken == apiToken); + if (session is null) + return null; + + ctx.Entry(session).State = EntityState.Detached; // Don't update the entity + session.VikingId = null; + session.UserId = userId; + + Model.PairData? pair = keyValueService.GetPairData(session, pairId); + + return keyValueService.ModelToSchema(pair); + } + [HttpPost] + [Produces("application/xml")] + [Route("ContentWebService.asmx/SetKeyValuePairByUserID")] + public IActionResult SetKeyValuePairByUserID([FromForm] string apiToken, [FromForm] int pairId, [FromForm] string userId, [FromForm] string contentXML) { + Schema.PairData schemaData = XmlUtil.DeserializeXml(contentXML); + + // Get the session + Session? session = ctx.Sessions.FirstOrDefault(s => s.ApiToken == apiToken); + if (session is null || string.IsNullOrEmpty(userId)) + return Ok(false); + + ctx.Entry(session).State = EntityState.Detached; // Don't update the entity + session.VikingId = null; + session.UserId = userId; + + bool result = keyValueService.SetPairData(session, pairId, schemaData); + + return Ok(result); + } + } diff --git a/src/Model/DBContext.cs b/src/Model/DBContext.cs index c750506..d0ef20f 100644 --- a/src/Model/DBContext.cs +++ b/src/Model/DBContext.cs @@ -5,6 +5,8 @@ public class DBContext : DbContext { public DbSet Users { get; set; } = null!; public DbSet Vikings { get; set; } = null!; public DbSet Sessions { get; set; } = null!; + public DbSet Pairs { get; set; } = null!; + public DbSet PairData { get; set; } = null!; public string DbPath { get; } public DBContext() { @@ -29,5 +31,16 @@ public class DBContext : DbContext { builder.Entity().HasMany(u => u.Sessions) .WithOne(e => e.Viking); + builder.Entity() + .HasKey(e => e.Id); + + builder.Entity().HasMany(p => p.Pairs) + .WithOne(e => e.PairData); + + builder.Entity() + .HasOne(p => p.PairData) + .WithMany(pd => pd.Pairs) + .HasForeignKey(p => p.MasterId) + .HasPrincipalKey(e => e.Id); } } diff --git a/src/Model/Pair.cs b/src/Model/Pair.cs new file mode 100644 index 0000000..3bb120a --- /dev/null +++ b/src/Model/Pair.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace sodoff.Model; + +public class Pair { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + + public string Key { get; set; } + + public string Value { get; set; } + + public int MasterId { get; set; } + + public virtual PairData PairData { get; set; } +} diff --git a/src/Model/PairData.cs b/src/Model/PairData.cs new file mode 100644 index 0000000..c5af83d --- /dev/null +++ b/src/Model/PairData.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace sodoff.Model; + +[Index(nameof(UserId))] +[Index(nameof(VikingId))] +public class PairData { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + + public int PairId { get; set; } + + public string? UserId { get; set; } + + public string? VikingId { get; set; } + + public virtual ICollection Pairs { get; set; } +} diff --git a/src/Program.cs b/src/Program.cs index 7ff12ca..6992087 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Mvc.Formatters; using sodoff.Model; +using sodoff.Services; using System.Xml; var builder = WebApplication.CreateBuilder(args); @@ -8,8 +9,10 @@ var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(options => { options.OutputFormatters.Add(new XmlSerializerOutputFormatter(new XmlWriterSettings() { OmitXmlDeclaration = false })); + options.OutputFormatters.RemoveType(); }); builder.Services.AddDbContext(); +builder.Services.AddScoped(); var app = builder.Build(); diff --git a/src/Schema/Pair.cs b/src/Schema/Pair.cs new file mode 100644 index 0000000..f5924d5 --- /dev/null +++ b/src/Schema/Pair.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "Pair", Namespace = "")] +[Serializable] +public class Pair { + [XmlElement(ElementName = "PairKey")] + public string PairKey; + + [XmlElement(ElementName = "PairValue")] + public string PairValue; + + [XmlElement(ElementName = "UpdateDate")] + public DateTime UpdateDate; +} diff --git a/src/Schema/PairData.cs b/src/Schema/PairData.cs new file mode 100644 index 0000000..7c74102 --- /dev/null +++ b/src/Schema/PairData.cs @@ -0,0 +1,12 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "Pairs", Namespace = "", IsNullable = true)] +[Serializable] +public class PairData { + [XmlElement("Pair", IsNullable = true)] + public Pair[] Pairs { get; set; } +} + + diff --git a/src/Services/KeyValueService.cs b/src/Services/KeyValueService.cs new file mode 100644 index 0000000..10240c6 --- /dev/null +++ b/src/Services/KeyValueService.cs @@ -0,0 +1,79 @@ +using sodoff.Model; +using sodoff.Schema; + +namespace sodoff.Services; +public class KeyValueService { + + private readonly DBContext ctx; + + public KeyValueService(DBContext ctx) { + this.ctx = ctx; + } + + public Model.PairData? GetPairData(Session session, int pairId) { + Model.PairData? pair = null; + if (session.VikingId != null) + pair = ctx.PairData.FirstOrDefault(e => e.PairId == pairId && e.VikingId == session.VikingId); + else if (session.UserId != null) + pair = ctx.PairData.FirstOrDefault(e => e.PairId == pairId && e.UserId == session.UserId); + + return pair; + } + + public bool SetPairData(Session session, int pairId, Schema.PairData schemaData) { + // Get the pair + Model.PairData? pair = GetPairData(session, pairId); + + // Create the pair if it doesn't exist + bool exists = true; + if (pair is null) { + pair = new Model.PairData { + PairId = pairId, + UserId = session.UserId, + VikingId = session.VikingId, + Pairs = new List() + }; + exists = false; + } + + // Update or create the key-value pairs + foreach (var p in schemaData.Pairs) { + if (string.IsNullOrEmpty(p.PairValue)) + return false; // NOTE: The real server doesn't update anything if one value is empty + var pairItem = pair.Pairs.FirstOrDefault(e => e.Key == p.PairKey); + if (pairItem != null) + pairItem.Value = p.PairValue; + else { + pairItem = new Model.Pair { + Key = p.PairKey, + Value = p.PairValue + }; + pair.Pairs.Add(pairItem); + } + } + + if (exists) + ctx.PairData.Update(pair); + else + ctx.PairData.Add(pair); + ctx.SaveChanges(); + + return true; + } + + public Schema.PairData? ModelToSchema(Model.PairData? model) { + if (model is null) return null; + + // Convert to schema class + List pairs = new List(); + foreach (var p in model.Pairs) { + pairs.Add(new Schema.Pair { + PairKey = p.Key, + PairValue = p.Value + }); + } + + return new Schema.PairData { Pairs = pairs.ToArray() }; + } + +}