From d4517b459ba5b422cb311c3b26429ec60de6cc5c Mon Sep 17 00:00:00 2001 From: hictooth Date: Sat, 17 Jun 2023 22:17:36 +0100 Subject: [PATCH 1/4] child registration and login --- .../Common/AuthenticationController.cs | 66 +++++++++++++++--- src/Controllers/Common/ContentController.cs | 54 +++++++++++++++ src/Controllers/Common/ProfileController.cs | 56 +++++++++++++++ .../Common/RegistrationController.cs | 38 +++++++++++ src/Model/DBContext.cs | 8 +++ src/Model/Session.cs | 9 ++- src/Model/Viking.cs | 17 +++++ src/Schema/AvatarData.cs | 23 +++++++ src/Schema/AvatarDataPart.cs | 29 ++++++++ src/Schema/AvatarDataPartOffset.cs | 14 ++++ src/Schema/AvatarDisplayData.cs | 28 ++++++++ src/Schema/AvatarPartAttribute.cs | 14 ++++ src/Schema/DisplayNameUniqueResponse.cs | 11 +++ src/Schema/GameData.cs | 41 +++++++++++ src/Schema/GameDataSummary.cs | 29 ++++++++ src/Schema/NameCategory.cs | 18 +++++ src/Schema/NameValidationRequest.cs | 13 ++++ src/Schema/NameValidationResponse.cs | 13 ++++ src/Schema/NameValidationResult.cs | 18 +++++ src/Schema/ProfileTag.cs | 17 +++++ src/Schema/ProfileUserAnswer.cs | 14 ++++ src/Schema/RewardMultiplier.cs | 17 +++++ src/Schema/SubscriptionNotification.cs | 9 +++ src/Schema/SubscriptionNotificationType.cs | 13 ++++ src/Schema/SuggestionResult.cs | 11 +++ src/Schema/UserAchievementInfo.cs | 26 +++++++ src/Schema/UserAnswerData.cs | 14 ++++ src/Schema/UserGrade.cs | 20 ++++++ src/Schema/UserProfileData.cs | 47 +++++++++++++ src/Schema/UserProfileGroupData.cs | 25 +++++++ src/Schema/UserProfileTag.cs | 23 +++++++ src/Schema/UserSubscriptionInfo.cs | 68 +++++++++++++++++++ src/Schema/UserSubscriptionProvider.cs | 27 ++++++++ 33 files changed, 818 insertions(+), 12 deletions(-) create mode 100644 src/Controllers/Common/ContentController.cs create mode 100644 src/Controllers/Common/ProfileController.cs create mode 100644 src/Model/Viking.cs create mode 100644 src/Schema/AvatarData.cs create mode 100644 src/Schema/AvatarDataPart.cs create mode 100644 src/Schema/AvatarDataPartOffset.cs create mode 100644 src/Schema/AvatarDisplayData.cs create mode 100644 src/Schema/AvatarPartAttribute.cs create mode 100644 src/Schema/DisplayNameUniqueResponse.cs create mode 100644 src/Schema/GameData.cs create mode 100644 src/Schema/GameDataSummary.cs create mode 100644 src/Schema/NameCategory.cs create mode 100644 src/Schema/NameValidationRequest.cs create mode 100644 src/Schema/NameValidationResponse.cs create mode 100644 src/Schema/NameValidationResult.cs create mode 100644 src/Schema/ProfileTag.cs create mode 100644 src/Schema/ProfileUserAnswer.cs create mode 100644 src/Schema/RewardMultiplier.cs create mode 100644 src/Schema/SubscriptionNotification.cs create mode 100644 src/Schema/SubscriptionNotificationType.cs create mode 100644 src/Schema/SuggestionResult.cs create mode 100644 src/Schema/UserAchievementInfo.cs create mode 100644 src/Schema/UserAnswerData.cs create mode 100644 src/Schema/UserGrade.cs create mode 100644 src/Schema/UserProfileData.cs create mode 100644 src/Schema/UserProfileGroupData.cs create mode 100644 src/Schema/UserProfileTag.cs create mode 100644 src/Schema/UserSubscriptionInfo.cs create mode 100644 src/Schema/UserSubscriptionProvider.cs diff --git a/src/Controllers/Common/AuthenticationController.cs b/src/Controllers/Common/AuthenticationController.cs index f125deb..3a23995 100644 --- a/src/Controllers/Common/AuthenticationController.cs +++ b/src/Controllers/Common/AuthenticationController.cs @@ -42,7 +42,7 @@ public class AuthenticationController : Controller { return Ok(new ParentLoginInfo { Status = MembershipUserStatus.InvalidPassword }); } - // Create seession + // Create session Session session = new Session { User = user, ApiToken = Guid.NewGuid().ToString() @@ -68,15 +68,33 @@ public class AuthenticationController : Controller { [Produces("application/xml")] [Route("AuthenticationWebService.asmx/GetUserInfoByApiToken")] public IActionResult GetUserInfoByApiToken([FromForm] string apiToken) { + // First check if this is a user session User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.User; + if (user is not null) { + return Ok(new UserInfo { + UserID = user.Id, + Username = user.Username, + MultiplayerEnabled = true, + Age = 24, + OpenChatEnabled = true + }); + } - return Ok(new UserInfo { - UserID = user.Id, - Username = user.Username, - MultiplayerEnabled = true, - Age = 24, - OpenChatEnabled = true - }); + // Then check if this is a viking session + Viking? viking = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.Viking; + if (viking is not null) + { + return Ok(new UserInfo { + UserID = viking.Id, + Username = viking.Name, + MultiplayerEnabled = true, + Age = 24, + OpenChatEnabled = true + }); + } + + // Otherwise, this is a bad session, return empty UserInfo + return Ok(new UserInfo {}); } [HttpPost] @@ -84,8 +102,38 @@ public class AuthenticationController : Controller { [Route("AuthenticationWebService.asmx/IsValidApiToken_V2")] public IActionResult IsValidApiToken([FromForm] string apiToken) { User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.User; - if (user is null) + Viking? viking = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.Viking; + if (user is null && viking is null) return Ok(ApiTokenStatus.TokenNotFound); return Ok(ApiTokenStatus.TokenValid); } + + // This is more of a "create session for viking", rather than "login child" + [HttpPost] + [Produces("application/xml")] + [Route("AuthenticationWebService.asmx/LoginChild")] + [DecryptRequest("childUserId")] + [EncryptResponse] + public IActionResult LoginChild([FromForm] string parentApiToken) { + User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == parentApiToken)?.User; + if (user is null) { + // Return empty response + return Ok(); + } + + // Find the viking + string? childUserId = Request.Form["parentLoginData"]; + Viking? viking = ctx.Vikings.FirstOrDefault(e => e.Id == childUserId); + + // Create session + Session session = new Session { + Viking = viking, + ApiToken = Guid.NewGuid().ToString() + }; + ctx.Sessions.Add(session); + ctx.SaveChanges(); + + // Return back the api token + return Ok(session.ApiToken); + } } diff --git a/src/Controllers/Common/ContentController.cs b/src/Controllers/Common/ContentController.cs new file mode 100644 index 0000000..848f903 --- /dev/null +++ b/src/Controllers/Common/ContentController.cs @@ -0,0 +1,54 @@ +using Microsoft.AspNetCore.Mvc; +using sodoff.Attributes; +using sodoff.Model; +using sodoff.Schema; +using sodoff.Util; + +namespace sodoff.Controllers.Common; +public class ContentController : Controller { + + private readonly DBContext ctx; + public ContentController(DBContext ctx) { + this.ctx = ctx; + } + + [HttpPost] + [Produces("application/xml")] + [Route("ContentWebService.asmx/GetDefaultNameSuggestion")] + public IActionResult GetDefaultNameSuggestion() { + // TODO: generate random names, and ensure they aren't already taken + string[] suggestions = new string[] { "dragon1", "dragon2", "dragon3" }; + return Ok(new DisplayNameUniqueResponse { + Suggestions = new SuggestionResult { + Suggestion = suggestions + } + }); + } + + [HttpPost] + [Produces("application/xml")] + [Route("V2/ContentWebService.asmx/ValidateName")] + [EncryptResponse] + public IActionResult ValidateName([FromForm] string apiToken,[FromForm] string nameValidationRequest) { + User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.User; + if (user is null) { + // TODO: better error handling than just replying not unique + return Ok(new NameValidationResponse { Result = NameValidationResult.NotUnique }); + } + + // Check if name populated + NameValidationRequest request = XmlUtil.DeserializeXml(nameValidationRequest); + + if (request.Category == NameCategory.Default) { + // This is an avatar we are checking + // Check if viking exists + bool exists = ctx.Vikings.Count(e => e.Name == request.Name) > 0; + NameValidationResult result = exists ? NameValidationResult.NotUnique : NameValidationResult.Ok; + return Ok(new NameValidationResponse { Result = result}); + + } else { + // TODO: pets, groups, default + return Ok(); + } + } +} diff --git a/src/Controllers/Common/ProfileController.cs b/src/Controllers/Common/ProfileController.cs new file mode 100644 index 0000000..31bafef --- /dev/null +++ b/src/Controllers/Common/ProfileController.cs @@ -0,0 +1,56 @@ +using Microsoft.AspNetCore.Mvc; +using sodoff.Attributes; +using sodoff.Model; +using sodoff.Schema; +using sodoff.Util; + +namespace sodoff.Controllers.Common; +public class ProfileController : Controller { + + private readonly DBContext ctx; + public ProfileController(DBContext ctx) { + this.ctx = ctx; + } + + [HttpPost] + [Produces("application/xml")] + [Route("ProfileWebService.asmx/GetUserProfileByUserID")] + public IActionResult GetUserProfileByUserID([FromForm] string apiToken, [FromForm] string userId) { + User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.User; + if (user is null) { + // TODO: what response for not logged in? + return Ok(); + } + + Viking? viking = ctx.Vikings.FirstOrDefault(e => e.Id == userId); + + return Ok(new UserProfileData { + ID = viking.Id, + AvatarInfo = new AvatarDisplayData { + UserInfo = new UserInfo { + UserID = viking.Id, + ParentUserID = user.Id, + Username = viking.Name, + MultiplayerEnabled = true, + GenderID = Gender.Male, + OpenChatEnabled = true, + CreationDate = DateTime.Now + }, + UserSubscriptionInfo = new UserSubscriptionInfo { SubscriptionTypeID = 2 }, // TODO: figure out what this is + Achievements = new UserAchievementInfo[] { + new UserAchievementInfo { + UserID = Guid.Parse(viking.Id), + AchievementPointTotal = 0, + RankID = 1, + PointTypeID = 1, // TODO: what is this? + } + } + }, + AchievementCount = 0, + MythieCount = 0, + AnswerData = new UserAnswerData { UserID = viking.Id }, + GameCurrency = 0, + CashCurrency = 0 + }); + } +} diff --git a/src/Controllers/Common/RegistrationController.cs b/src/Controllers/Common/RegistrationController.cs index e5f1f2f..800a23a 100644 --- a/src/Controllers/Common/RegistrationController.cs +++ b/src/Controllers/Common/RegistrationController.cs @@ -54,4 +54,42 @@ public class RegistrationController : Controller { return Ok(response); } + + [HttpPost] + [Produces("application/xml")] + [Route("V4/RegistrationWebService.asmx/RegisterChild")] + [DecryptRequest("childRegistrationData")] + [EncryptResponse] + public IActionResult RegisterChild([FromForm] string parentApiToken) { + User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == parentApiToken)?.User; + if (user is null) { + return Ok(new RegistrationResult{ + Status = MembershipUserStatus.InvalidApiToken + }); + } + + // Check if name populated + ChildRegistrationData data = XmlUtil.DeserializeXml(Request.Form["childRegistrationData"]); + if (String.IsNullOrWhiteSpace(data.ChildName)) { + return Ok(new RegistrationResult { Status = MembershipUserStatus.ValidationError }); + } + + // Check if viking exists + if (ctx.Vikings.Count(e => e.Name == data.ChildName) > 0) { + return Ok(new RegistrationResult { Status = MembershipUserStatus.DuplicateUserName }); + } + + Viking v = new Viking { + Id = Guid.NewGuid().ToString(), + Name = data.ChildName, + User = user, + }; + ctx.Vikings.Add(v); + ctx.SaveChanges(); + + return Ok(new RegistrationResult { + UserID = v.Id, + Status = MembershipUserStatus.Success + }); + } } diff --git a/src/Model/DBContext.cs b/src/Model/DBContext.cs index cd9ae61..c750506 100644 --- a/src/Model/DBContext.cs +++ b/src/Model/DBContext.cs @@ -3,6 +3,7 @@ namespace sodoff.Model; public class DBContext : DbContext { public DbSet Users { get; set; } = null!; + public DbSet Vikings { get; set; } = null!; public DbSet Sessions { get; set; } = null!; public string DbPath { get; } @@ -18,8 +19,15 @@ public class DBContext : DbContext { .WithMany(e => e.Sessions) .HasForeignKey(e => e.UserId); + builder.Entity().HasOne(s => s.Viking) + .WithMany(e => e.Sessions) + .HasForeignKey(e => e.VikingId); + builder.Entity().HasMany(u => u.Sessions) .WithOne(e => e.User); + builder.Entity().HasMany(u => u.Sessions) + .WithOne(e => e.Viking); + } } diff --git a/src/Model/Session.cs b/src/Model/Session.cs index b42d468..9fb9584 100644 --- a/src/Model/Session.cs +++ b/src/Model/Session.cs @@ -5,8 +5,11 @@ public class Session { [Key] public string ApiToken { get; set; } = null!; - [Required] - public string UserId { get; set; } = null!; + public string? UserId { get; set; } - public virtual User User { get; set; } = null!; + public string? VikingId { get; set; } + + public virtual User? User { get; set; } + + public virtual Viking? Viking { get; set; } } diff --git a/src/Model/Viking.cs b/src/Model/Viking.cs new file mode 100644 index 0000000..ff4b7a8 --- /dev/null +++ b/src/Model/Viking.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace sodoff.Model; +public class Viking { + [Key] + public string Id { get; set; } = null!; + + [Required] + public string Name { get; set; } = null!; + + [Required] + public string UserId { get; set; } = null!; + + public virtual ICollection Sessions { get; set; } = null!; + + public virtual User User { get; set; } = null!; +} diff --git a/src/Schema/AvatarData.cs b/src/Schema/AvatarData.cs new file mode 100644 index 0000000..6b50534 --- /dev/null +++ b/src/Schema/AvatarData.cs @@ -0,0 +1,23 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "AvatarData", Namespace = "")] +[Serializable] +public class AvatarData +{ + [XmlElement(ElementName = "IsSuggestedAvatarName", IsNullable = true)] + + public int? Id; + + public string DisplayName; + + [XmlElement(ElementName = "Part")] + public AvatarDataPart[] Part; + + [XmlElement(ElementName = "Gender")] + public Gender GenderType; + + [XmlElement(ElementName = "UTD", IsNullable = true)] + public bool? SetUserNameToDisplayName; +} diff --git a/src/Schema/AvatarDataPart.cs b/src/Schema/AvatarDataPart.cs new file mode 100644 index 0000000..422e771 --- /dev/null +++ b/src/Schema/AvatarDataPart.cs @@ -0,0 +1,29 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "AvatarDataPart", Namespace = "")] +[Serializable] +public class AvatarDataPart +{ + public string PartType; + + [XmlArrayItem("Offset")] + public AvatarDataPartOffset[] Offsets; + + [XmlArrayItem("Geometry")] + public string[] Geometries; + + [XmlArrayItem("Texture")] + public string[] Textures; + + [XmlArrayItem("Attribute")] + public AvatarPartAttribute[] Attributes; + + [XmlElement(ElementName = "Uiid", IsNullable = true)] + public int? UserInventoryId; + + public const string SAVED_DEFAULT_PREFIX = "DEFAULT_"; + + public const string PLACEHOLDER = "PLACEHOLDER"; +} diff --git a/src/Schema/AvatarDataPartOffset.cs b/src/Schema/AvatarDataPartOffset.cs new file mode 100644 index 0000000..279d5bf --- /dev/null +++ b/src/Schema/AvatarDataPartOffset.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "AvatarDataPartOffset", Namespace = "")] +[Serializable] +public class AvatarDataPartOffset +{ + public float X; + + public float Y; + + public float Z; +} diff --git a/src/Schema/AvatarDisplayData.cs b/src/Schema/AvatarDisplayData.cs new file mode 100644 index 0000000..5447bbe --- /dev/null +++ b/src/Schema/AvatarDisplayData.cs @@ -0,0 +1,28 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +// Token: 0x02000472 RID: 1138 +[XmlRoot(ElementName = "AvatarDisplayData", Namespace = "", IsNullable = false)] +[Serializable] +public class AvatarDisplayData +{ + [XmlElement(ElementName = "AvatarData", IsNullable = true)] + public AvatarData AvatarData; + + [XmlElement(Namespace = "http://api.jumpstart.com/")] + public UserInfo UserInfo; + + [XmlElement(ElementName = "UserSubscriptionInfo")] + public UserSubscriptionInfo UserSubscriptionInfo; + + public UserAchievementInfo AchievementInfo; + + [XmlElement(ElementName = "Achievements")] + public UserAchievementInfo[] Achievements; + + [XmlElement(ElementName = "RewardMultipliers", IsNullable = true)] + public RewardMultiplier[] RewardMultipliers; + + public int RankID; +} diff --git a/src/Schema/AvatarPartAttribute.cs b/src/Schema/AvatarPartAttribute.cs new file mode 100644 index 0000000..a2fce14 --- /dev/null +++ b/src/Schema/AvatarPartAttribute.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "Attribute", Namespace = "")] +[Serializable] +public class AvatarPartAttribute +{ + [XmlElement(ElementName = "K")] + public string Key; + + [XmlElement(ElementName = "V")] + public string Value; +} diff --git a/src/Schema/DisplayNameUniqueResponse.cs b/src/Schema/DisplayNameUniqueResponse.cs new file mode 100644 index 0000000..83178be --- /dev/null +++ b/src/Schema/DisplayNameUniqueResponse.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "DisplayNameUniqueResponse", Namespace = "")] +[Serializable] +public class DisplayNameUniqueResponse +{ + [XmlElement(ElementName = "suggestions", IsNullable = true)] + public SuggestionResult Suggestions { get; set; } +} diff --git a/src/Schema/GameData.cs b/src/Schema/GameData.cs new file mode 100644 index 0000000..6c2c44f --- /dev/null +++ b/src/Schema/GameData.cs @@ -0,0 +1,41 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "GameData", Namespace = "")] +[Serializable] +public class GameData +{ + [XmlElement(ElementName = "RankID", IsNullable = true)] + public int? RankID; + + [XmlElement(ElementName = "IsMember")] + public bool IsMember; + + [XmlElement(ElementName = "UserName")] + public string UserName; + + [XmlElement(ElementName = "Value")] + public int Value; + + [XmlElement(ElementName = "DatePlayed", IsNullable = true)] + public DateTime? DatePlayed; + + [XmlElement(ElementName = "Win")] + public int Win; + + [XmlElement(ElementName = "Loss")] + public int Loss; + + [XmlElement(ElementName = "UserID")] + public Guid UserID; + + [XmlElement(ElementName = "ProductID", IsNullable = true)] + public int? ProductID; + + [XmlElement(ElementName = "PlatformID", IsNullable = true)] + public int? PlatformID; + + [XmlElement(ElementName = "FBIDS", IsNullable = true)] + public long? FacebookID; +} diff --git a/src/Schema/GameDataSummary.cs b/src/Schema/GameDataSummary.cs new file mode 100644 index 0000000..ac2a3c6 --- /dev/null +++ b/src/Schema/GameDataSummary.cs @@ -0,0 +1,29 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "GameDataSummary", Namespace = "")] +[Serializable] +public class GameDataSummary +{ + [XmlElement(ElementName = "GameDataList")] + public GameData[] GameDataList; + + [XmlElement(ElementName = "UserPosition", IsNullable = true)] + public int? UserPosition; + + [XmlElement(ElementName = "GameID")] + public int GameID; + + [XmlElement(ElementName = "IsMultiplayer")] + public bool IsMultiplayer; + + [XmlElement(ElementName = "Difficulty")] + public int Difficulty; + + [XmlElement(ElementName = "GameLevel")] + public int GameLevel; + + [XmlElement(ElementName = "Key")] + public string Key; +} diff --git a/src/Schema/NameCategory.cs b/src/Schema/NameCategory.cs new file mode 100644 index 0000000..70e6cbe --- /dev/null +++ b/src/Schema/NameCategory.cs @@ -0,0 +1,18 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +public enum NameCategory +{ + [XmlEnum("1")] + Avatar = 1, + + [XmlEnum("2")] + Pet, + + [XmlEnum("3")] + Group, + + [XmlEnum("4")] + Default +} diff --git a/src/Schema/NameValidationRequest.cs b/src/Schema/NameValidationRequest.cs new file mode 100644 index 0000000..5553cb9 --- /dev/null +++ b/src/Schema/NameValidationRequest.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "NameValidationRequest", Namespace = "")] +public class NameValidationRequest +{ + [XmlElement(ElementName = "Name")] + public string Name; + + [XmlElement(ElementName = "Category")] + public NameCategory Category; +} diff --git a/src/Schema/NameValidationResponse.cs b/src/Schema/NameValidationResponse.cs new file mode 100644 index 0000000..d7e8c0b --- /dev/null +++ b/src/Schema/NameValidationResponse.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "NameValidationResponse", Namespace = "")] +public class NameValidationResponse +{ + [XmlElement(ElementName = "ErrorMessage")] + public string ErrorMessage; + + [XmlElement(ElementName = "Category")] + public NameValidationResult Result; +} diff --git a/src/Schema/NameValidationResult.cs b/src/Schema/NameValidationResult.cs new file mode 100644 index 0000000..70ec7b0 --- /dev/null +++ b/src/Schema/NameValidationResult.cs @@ -0,0 +1,18 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +public enum NameValidationResult +{ + [XmlEnum("1")] + Ok = 1, + + [XmlEnum("2")] + Blocked, + + [XmlEnum("3")] + NotUnique, + + [XmlEnum("4")] + InvalidLength +} diff --git a/src/Schema/ProfileTag.cs b/src/Schema/ProfileTag.cs new file mode 100644 index 0000000..8242d61 --- /dev/null +++ b/src/Schema/ProfileTag.cs @@ -0,0 +1,17 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "PT", Namespace = "")] +[Serializable] +public class ProfileTag +{ + [XmlElement(ElementName = "ID")] + public int TagID; + + [XmlElement(ElementName = "NA", IsNullable = true)] + public string TagName; + + [XmlElement(ElementName = "VAL", IsNullable = true)] + public int? Value; +} diff --git a/src/Schema/ProfileUserAnswer.cs b/src/Schema/ProfileUserAnswer.cs new file mode 100644 index 0000000..5eed8a6 --- /dev/null +++ b/src/Schema/ProfileUserAnswer.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "Answers", IsNullable = true, Namespace = "")] +[Serializable] +public class ProfileUserAnswer +{ + [XmlElement(ElementName = "AID")] + public int AnswerID; + + [XmlElement(ElementName = "QID")] + public int QuestionID; +} diff --git a/src/Schema/RewardMultiplier.cs b/src/Schema/RewardMultiplier.cs new file mode 100644 index 0000000..53a23f9 --- /dev/null +++ b/src/Schema/RewardMultiplier.cs @@ -0,0 +1,17 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "ARM", IsNullable = true, Namespace = "")] +[Serializable] +public class RewardMultiplier +{ + [XmlElement(ElementName = "PT")] + public int PointTypeID; + + [XmlElement(ElementName = "MF")] + public int MultiplierFactor; + + [XmlElement(ElementName = "MET")] + public DateTime MultiplierEffectTime; +} diff --git a/src/Schema/SubscriptionNotification.cs b/src/Schema/SubscriptionNotification.cs new file mode 100644 index 0000000..adcb32d --- /dev/null +++ b/src/Schema/SubscriptionNotification.cs @@ -0,0 +1,9 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +public class SubscriptionNotification +{ + [XmlElement(ElementName = "Type")] + public SubscriptionNotificationType Type; +} diff --git a/src/Schema/SubscriptionNotificationType.cs b/src/Schema/SubscriptionNotificationType.cs new file mode 100644 index 0000000..b862d39 --- /dev/null +++ b/src/Schema/SubscriptionNotificationType.cs @@ -0,0 +1,13 @@ +using System; + +namespace sodoff.Schema; + +public enum SubscriptionNotificationType +{ + NONE, + SUBSCRIPTION_IN_GRACE_PERIOD, + SUBSCRIPTION_EXPIRED, + SUBSCRIPTION_ON_HOLD, + SUBSCRIPTION_RECOVERED, + SUBSCRIPTION_CANCELED +} diff --git a/src/Schema/SuggestionResult.cs b/src/Schema/SuggestionResult.cs new file mode 100644 index 0000000..2770f2f --- /dev/null +++ b/src/Schema/SuggestionResult.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "SuggestionResult", Namespace = "", IsNullable = false)] +[Serializable] +public class SuggestionResult +{ + [XmlElement(ElementName = "Suggestion", IsNullable = true)] + public string[] Suggestion; +} diff --git a/src/Schema/UserAchievementInfo.cs b/src/Schema/UserAchievementInfo.cs new file mode 100644 index 0000000..d8b2d75 --- /dev/null +++ b/src/Schema/UserAchievementInfo.cs @@ -0,0 +1,26 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "UAI", Namespace = "")] +[Serializable] +public class UserAchievementInfo +{ + [XmlElement(ElementName = "u")] + public Guid? UserID; + + [XmlElement(ElementName = "n")] + public string UserName; + + [XmlElement(ElementName = "a")] + public int? AchievementPointTotal; + + [XmlElement(ElementName = "r")] + public int RankID; + + [XmlElement(ElementName = "p")] + public int? PointTypeID; + + [XmlElement(ElementName = "FBUID", IsNullable = true)] + public long? FacebookUserID; +} diff --git a/src/Schema/UserAnswerData.cs b/src/Schema/UserAnswerData.cs new file mode 100644 index 0000000..d8c4668 --- /dev/null +++ b/src/Schema/UserAnswerData.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "Answer", IsNullable = true, Namespace = "")] +[Serializable] +public class UserAnswerData +{ + [XmlElement(ElementName = "ID")] + public string UserID; + + [XmlElement(ElementName = "Answers")] + public ProfileUserAnswer[] Answers; +} diff --git a/src/Schema/UserGrade.cs b/src/Schema/UserGrade.cs new file mode 100644 index 0000000..df8b20a --- /dev/null +++ b/src/Schema/UserGrade.cs @@ -0,0 +1,20 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "UGrad", Namespace = "", IsNullable = true)] +[Serializable] +public class UserGrade +{ + [XmlElement(ElementName = "UId", IsNullable = true)] + public Guid? UserID; + + [XmlElement(ElementName = "UGID")] + public int UserGradeID; + + [XmlElement(ElementName = "UGN")] + public string UserGradeName; + + [XmlElement(ElementName = "UGL", IsNullable = true)] + public bool? Locked; +} diff --git a/src/Schema/UserProfileData.cs b/src/Schema/UserProfileData.cs new file mode 100644 index 0000000..bb6fb07 --- /dev/null +++ b/src/Schema/UserProfileData.cs @@ -0,0 +1,47 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +// Token: 0x02000622 RID: 1570 +[XmlRoot(ElementName = "UserProfileDisplayData", IsNullable = true, Namespace = "")] +[Serializable] +public class UserProfileData +{ + public string ID; + + [XmlElement(ElementName = "Avatar")] + public AvatarDisplayData AvatarInfo; + + [XmlElement(ElementName = "Ach")] + public int AchievementCount; + + [XmlElement(ElementName = "Mth")] + public int MythieCount; + + [XmlElement(ElementName = "Answer", IsNullable = true)] + public UserAnswerData AnswerData; + + [XmlElement(ElementName = "Game", IsNullable = true)] + public GameDataSummary GameInfo; + + [XmlElement(ElementName = "gc", IsNullable = true)] + public int? GameCurrency; + + [XmlElement(ElementName = "cc", IsNullable = true)] + public int? CashCurrency; + + [XmlElement(ElementName = "BuddyCount", IsNullable = true)] + public int? BuddyCount; + + [XmlElement(ElementName = "ActivityCount", IsNullable = true)] + public int? ActivityCount; + + [XmlElement(ElementName = "Groups")] + public UserProfileGroupData[] Groups; + + [XmlElement(ElementName = "UPT")] + public UserProfileTag UserProfileTag; + + [XmlElement(ElementName = "UG", IsNullable = true)] + public UserGrade UserGradeData; +} diff --git a/src/Schema/UserProfileGroupData.cs b/src/Schema/UserProfileGroupData.cs new file mode 100644 index 0000000..207edf4 --- /dev/null +++ b/src/Schema/UserProfileGroupData.cs @@ -0,0 +1,25 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "UPGD", IsNullable = true, Namespace = "")] +public class UserProfileGroupData +{ + [XmlElement(ElementName = "GroupID")] + public string GroupID; + + [XmlElement(ElementName = "RoleID", IsNullable = true)] + public int? RoleID; + + [XmlElement(ElementName = "TypeID", IsNullable = true)] + public int? TypeID; + + [XmlElement(ElementName = "N")] + public string Name; + + [XmlElement(ElementName = "L")] + public string Logo; + + [XmlElement(ElementName = "C")] + public string Color; +} diff --git a/src/Schema/UserProfileTag.cs b/src/Schema/UserProfileTag.cs new file mode 100644 index 0000000..edeedb5 --- /dev/null +++ b/src/Schema/UserProfileTag.cs @@ -0,0 +1,23 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "UPT", Namespace = "")] +[Serializable] +public class UserProfileTag +{ + [XmlElement(ElementName = "Date")] + public DateTime CreateDate; + + [XmlElement(ElementName = "PGID")] + public int ProductGroupID; + + [XmlElement(ElementName = "User")] + public Guid UserID; + + [XmlElement(ElementName = "ID")] + public int UserProfileTagID; + + [XmlElement(ElementName = "ProfileTag")] + public List ProfileTags; +} diff --git a/src/Schema/UserSubscriptionInfo.cs b/src/Schema/UserSubscriptionInfo.cs new file mode 100644 index 0000000..9c282fc --- /dev/null +++ b/src/Schema/UserSubscriptionInfo.cs @@ -0,0 +1,68 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "UserSubscriptionInfo", IsNullable = true, Namespace = "")] +[Serializable] +public class UserSubscriptionInfo +{ + [XmlElement(ElementName = "PID")] + public string PID; + + [XmlElement(ElementName = "BillFrequency", IsNullable = true)] + public short? BillFrequency; + + [XmlElement(ElementName = "CardExpirationDate", IsNullable = true)] + public DateTime? CardExpirationDate; + + [XmlElement(ElementName = "CardReferenceNumber")] + public string CardReferenceNumber; + + [XmlElement(ElementName = "IsActive", IsNullable = true)] + public bool? IsActive; + + [XmlElement(ElementName = "LastBillDate", IsNullable = true)] + public DateTime? LastBillDate; + + [XmlElement(ElementName = "MembershipID", IsNullable = true)] + public int? MembershipID; + + [XmlElement(ElementName = "ProfileCurrency")] + public string ProfileCurrency; + + [XmlElement(ElementName = "ProfileID")] + public string ProfileID; + + [XmlElement(ElementName = "Recurring", IsNullable = true)] + public bool? Recurring; + + [XmlElement(ElementName = "RecurringAmount", IsNullable = true)] + public float? RecurringAmount; + + [XmlElement(ElementName = "Status")] + public string Status; + + [XmlElement(ElementName = "SubscriptionDisplayName")] + public string SubscriptionDisplayName; + + [XmlElement(ElementName = "SubscriptionEndDate", IsNullable = true)] + public DateTime? SubscriptionEndDate; + + [XmlElement(ElementName = "SubscriptionID", IsNullable = true)] + public int? SubscriptionID; + + [XmlElement(ElementName = "SubscriptionPlanID", IsNullable = true)] + public int? SubscriptionPlanID; + + [XmlElement(ElementName = "SubscriptionProvider")] + public UserSubscriptionProvider SubscriptionProvider; + + [XmlElement(ElementName = "SubscriptionTypeID", IsNullable = true)] + public int? SubscriptionTypeID; + + [XmlElement(ElementName = "UserID", IsNullable = true)] + public string UserID; + + [XmlElement(ElementName = "UserSubscriptionNotification", IsNullable = true)] + public SubscriptionNotification UserSubscriptionNotification; +} diff --git a/src/Schema/UserSubscriptionProvider.cs b/src/Schema/UserSubscriptionProvider.cs new file mode 100644 index 0000000..94fd19e --- /dev/null +++ b/src/Schema/UserSubscriptionProvider.cs @@ -0,0 +1,27 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +public class UserSubscriptionProvider +{ + [XmlElement(ElementName = "ItemID", IsNullable = false)] + public string ItemID; + + [XmlElement(ElementName = "MembershipPurchaseAllowed", IsNullable = false)] + public bool MembershipPurchaseAllowed; + + [XmlElement(ElementName = "ProductID", IsNullable = false)] + public int ProductID; + + [XmlElement(ElementName = "Provider", IsNullable = false)] + public int Provider; + + [XmlElement(ElementName = "ReceiptData", IsNullable = false)] + public string ReceiptData; + + [XmlElement(ElementName = "Recurring", IsNullable = false)] + public bool Recurring; + + [XmlElement(ElementName = "UserID", IsNullable = false)] + public Guid UserID; +} From 117c15a43970de1f5dc667d5672e92622ba6cae1 Mon Sep 17 00:00:00 2001 From: hictooth Date: Sat, 17 Jun 2023 23:06:09 +0100 Subject: [PATCH 2/4] fix session saving --- src/Controllers/Common/AuthenticationController.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Controllers/Common/AuthenticationController.cs b/src/Controllers/Common/AuthenticationController.cs index 3a23995..5a437e9 100644 --- a/src/Controllers/Common/AuthenticationController.cs +++ b/src/Controllers/Common/AuthenticationController.cs @@ -112,18 +112,20 @@ public class AuthenticationController : Controller { [HttpPost] [Produces("application/xml")] [Route("AuthenticationWebService.asmx/LoginChild")] - [DecryptRequest("childUserId")] + [DecryptRequest("childUserID")] [EncryptResponse] - public IActionResult LoginChild([FromForm] string parentApiToken) { + public IActionResult LoginChild10([FromForm] string parentApiToken) { User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == parentApiToken)?.User; if (user is null) { - // Return empty response return Ok(); } // Find the viking - string? childUserId = Request.Form["parentLoginData"]; - Viking? viking = ctx.Vikings.FirstOrDefault(e => e.Id == childUserId); + string? childUserID = Request.Form["childUserID"]; + Viking? viking = ctx.Vikings.FirstOrDefault(e => e.Id == childUserID); + if (viking is null) { + return Ok(); + } // Create session Session session = new Session { From 4c811cff42478ecbd846670c468a4d7cd44a8aac Mon Sep 17 00:00:00 2001 From: hictooth Date: Sat, 17 Jun 2023 23:37:21 +0100 Subject: [PATCH 3/4] rename LoginChild back --- src/Controllers/Common/AuthenticationController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controllers/Common/AuthenticationController.cs b/src/Controllers/Common/AuthenticationController.cs index 5a437e9..5e0a990 100644 --- a/src/Controllers/Common/AuthenticationController.cs +++ b/src/Controllers/Common/AuthenticationController.cs @@ -114,7 +114,7 @@ public class AuthenticationController : Controller { [Route("AuthenticationWebService.asmx/LoginChild")] [DecryptRequest("childUserID")] [EncryptResponse] - public IActionResult LoginChild10([FromForm] string parentApiToken) { + public IActionResult LoginChild([FromForm] string parentApiToken) { User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == parentApiToken)?.User; if (user is null) { return Ok(); From 6f32e5a8ff99ef78bfb659d96a0bb0f746e4e40f Mon Sep 17 00:00:00 2001 From: Spirtix Date: Sun, 18 Jun 2023 11:03:13 +0200 Subject: [PATCH 4/4] don't encrypt ValidateName response; update mitm endpoints --- mitm-redirect.py | 4 ++-- src/Controllers/Common/ContentController.cs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mitm-redirect.py b/mitm-redirect.py index 91a8547..ec99e3f 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'] + methods = ['GetRules', 'LoginParent', 'RegisterParent', 'GetSubscriptionInfo', 'GetUserInfoByApiToken', 'IsValidApiToken_V2', 'ValidateName', 'GetDefaultNameSuggestion', 'RegisterChild', 'GetProfileByUserId', 'LoginChild', 'GetUserProfileByUserID'] for method in methods: if method in path: return True @@ -15,7 +15,7 @@ class LocalRedirect: print('Loaded redirect addon') def request(self, flow: mitmproxy.http.HTTPFlow): - if 'common.api.jumpstart.com' in flow.request.pretty_host and routable(flow.request.path): + if 'api.jumpstart.com' in flow.request.pretty_host and routable(flow.request.path): flow.request.host = "localhost" flow.request.scheme = 'http' flow.request.port = 5000 diff --git a/src/Controllers/Common/ContentController.cs b/src/Controllers/Common/ContentController.cs index 848f903..c9b31a7 100644 --- a/src/Controllers/Common/ContentController.cs +++ b/src/Controllers/Common/ContentController.cs @@ -28,7 +28,6 @@ public class ContentController : Controller { [HttpPost] [Produces("application/xml")] [Route("V2/ContentWebService.asmx/ValidateName")] - [EncryptResponse] public IActionResult ValidateName([FromForm] string apiToken,[FromForm] string nameValidationRequest) { User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.User; if (user is null) {