public KeyStore Sign(KeyUsage usage, int quota, AccountData account, DateTime from, DateTime to) { var key = new byte[GlobalConfig.TOKEN_LENGTH]; _cryptoRandom.GetBytes(key); if (usage.Usage == 0) { throw new InvalidOperationException("Key Usage Not Recorded"); } //var keyStore = _context.KeyStore.CreateProxy(); var keyStore = new KeyStore(); { keyStore.Key = key; keyStore.HoldingAccountNavigation = account; keyStore.ValidFrom = from; keyStore.ValidUntil = to; keyStore.LastActive = _time.UtcNow; keyStore.UsageNavigation = usage; keyStore.ReuseCounter = -1; keyStore.Quota = quota; }; _context.KeyStore.Add(keyStore); _context.SaveChanges(); _auth.EnsureKey(keyStore, usage, GlobalOperation.GENERATE_APIKEY, 0, "Sign New Key", "N/A"); return(keyStore); }
public string Sign(TokenPayload payload) { //var issue = _context.TokenIssueList.CreateProxy(); var issue = new TokenIssueList(); { issue.HoldingAccount = payload.AccountId; issue.Reason = (int)payload.Purpose; issue.IssueTime = _time.UtcNow; issue.ValidUntil = payload.ValidTo; }; _context.TokenIssueList.Add(issue); _context.SaveChanges(); payload.TokenId = issue.TokenSerial; return(_signer.Encode(payload.ToPayloadBytes())); }
public ActionResult <MilvanethProtocol> SessionCreate(MilvanethProtocol data) { if (!(data?.Data is AuthRequest request) || !request.Check()) { return(new MilvanethProtocol { Context = null, Data = new AuthResponse { Message = GlobalMessage.DATA_INVALID_INPUT, ReportTime = _time.SafeNow, } }); } try { var key = _context.KeyStore .Include(x => x.HoldingAccountNavigation) .ThenInclude(x => x.PrivilegeLevelNavigation) .Include(x => x.UsageNavigation) .Single(x => x.Key.SequenceEqual(request.AuthToken)); _auth.EnsureKey(key, new KeyUsage { CreateSession = true }, GlobalOperation.SESSION_CREATE, 0, "Create session via session/create", _accessor.GetIp()); var account = key.HoldingAccountNavigation; _auth.EnsureAccount(account, new PrivilegeConfig { AccessData = true }, GlobalOperation.SESSION_CREATE, 0, "Create session via session/create", _accessor.GetIp()); var renew = _api.Sign(_renewToken, 1, account, _time.UtcNow, _time.UtcNow.AddSeconds(GlobalConfig.TOKEN_RENEW_LIFE_TIME)); var access = _token.Sign(new TokenPayload(_time.UtcNow.AddSeconds(GlobalConfig.TOKEN_DATA_LIFE_TIME), account.AccountId, TokenPurpose.AccessToken, renew.KeyId)); _context.SaveChanges(); return(new MilvanethProtocol { Context = null, Data = new AuthResponse { Message = GlobalMessage.OK_SUCCESS, ReportTime = _time.SafeNow, RenewToken = renew.Key, SessionToken = access } }); } catch (Exception e) { Log.Error(e, "Error in SESSION/CREATE"); return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { Message = GlobalMessage.OP_INVALID, ReportTime = _time.SafeNow, } }); } }
public IEnumerable <RetainerData> CommitRange(long accountId, IEnumerable <RetainerData> data, bool saveChange) { if (data == null) { throw new InvalidOperationException(); } var snapshot = data.ToImmutableList(); if (snapshot.Any(x => x.RetainerId <= 0)) { throw new InvalidOperationException(); } var count = snapshot.Count; if (count <= 0) { return(null); } if (count == 1) { return new[] { Commit(accountId, data.Single(), saveChange) } } ; if (count >= 2) { var search = snapshot.Select(x => x.RetainerId); var source = _context.RetainerData.Where(x => search.Contains(x.RetainerId)); var sourceMirror = source.AsNoTracking().ToImmutableList(); var remain = snapshot.Where(x => sourceMirror.All(y => y.RetainerId != x.RetainerId)); //var remain = snapshot.Where(x => source.AsNoTracking().All(y => y.RetainerId != x.RetainerId)); foreach (var entity in source) { var delta = snapshot.Single(x => x.RetainerId == entity.RetainerId); if (delta.RetainerName != null && delta.RetainerName != entity.RetainerName) { _context.DataLog.Add(new DataLog { FromValue = entity.RetainerName, ToValue = delta.RetainerName, Operator = accountId, RecordId = entity.RetainerId, TableColumn = GlobalOperation.COLUMN_RETAINER_NAME, ReportTime = _time.UtcNow }); entity.RetainerName = delta.RetainerName; } if (delta.Character != 0 && delta.Character != entity.Character) { _context.DataLog.Add(new DataLog { FromValue = entity.Character.ToString(), ToValue = delta.Character.ToString(), Operator = accountId, RecordId = entity.RetainerId, TableColumn = GlobalOperation.COLUMN_RETAINER_CHARA, ReportTime = _time.UtcNow }); entity.Character = delta.Character; } if (delta.World != 0 && delta.World != entity.World) { _context.DataLog.Add(new DataLog { FromValue = entity.World.ToString(), ToValue = delta.World.ToString(), Operator = accountId, RecordId = entity.RetainerId, TableColumn = GlobalOperation.COLUMN_RETAINER_WORLD, ReportTime = _time.UtcNow }); entity.World = delta.World; } if (delta.Location != 0 && delta.Location != entity.Location) { _context.DataLog.Add(new DataLog { FromValue = entity.Location.ToString(), ToValue = delta.Location.ToString(), Operator = accountId, RecordId = entity.RetainerId, TableColumn = GlobalOperation.COLUMN_RETAINER_LOCATION, ReportTime = _time.UtcNow }); entity.Location = delta.Location; } if (delta.Inventory != null) { entity.Inventory = delta.Inventory; } } if (remain.Any()) { _context.RetainerData.AddRange(remain); _context.DataLog.Add(new DataLog { FromValue = null, ToValue = "INITIAL COMMIT MULTIPLE", Operator = accountId, RecordId = 0, TableColumn = GlobalOperation.COLUMN_RETAINER_NEW, ReportTime = _time.UtcNow }); } if (saveChange) { _context.SaveChanges(); } return(source.Concat(remain)); } return(null); }
public ActionResult <MilvanethProtocol> AuthStart(MilvanethProtocol data) { if (!(data?.Data is ClientChallenge challenge) || !challenge.Check()) { return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { Message = GlobalMessage.DATA_INVALID_INPUT, ReportTime = _time.SafeNow, } }); } if (!_pow.Verify(challenge.ProofOfWork) && _pow.ConditionalGenerate(out var requirement)) { return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { Message = GlobalMessage.RATE_POW_REQUIRED, ProofOfWork = requirement, ReportTime = _time.SafeNow, } }); } try { var accountData = _context.AccountData.Include(x => x.PrivilegeLevelNavigation).SingleOrDefault(x => x.AccountName == challenge.Username); if (accountData == null) { return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { Message = GlobalMessage.DATA_NO_SUCH_USER, ReportTime = _time.SafeNow, } }); } if (accountData.PrivilegeLevel == GlobalConfig.ACCOUNT_BLOCKED_LEVEL) { return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { Message = GlobalMessage.OP_SANCTION, ReportTime = _time.SafeNow, } }); } if (accountData.HasSuspended() && accountData.SuspendUntil >= _time.UtcNow) { return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { Message = GlobalMessage.OP_ACCOUNT_SUSPENDED, ReportTime = _time.SafeNow, } }); } if (accountData.PasswordRetry > GlobalConfig.ACCOUNT_PASSWORD_RETRY_TOLERANCE && accountData.LastRetry.HasValue && (_time.UtcNow - accountData.LastRetry.Value).Seconds < GlobalConfig.ACCOUNT_PASSWORD_RETRY_COOLDOWN) { return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { Message = GlobalMessage.OP_PASSWORD_RETRY_TOO_MUCH, ReportTime = _time.SafeNow, } }); } _auth.EnsureAccount(accountData, new PrivilegeConfig { Login = true }, GlobalOperation.AUTH_START, 0, "Start login via auth/start", _accessor.GetIp()); var session = _srp.DoServerResponse(accountData.AccountId, accountData.GroupParam, accountData.Verifier, out var token); _context.AccountData.Update(accountData); _context.SaveChanges(); return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { GroupParam = accountData.GroupParam, Message = GlobalMessage.OK_SUCCESS, ReportTime = _time.SafeNow, Salt = accountData.Salt, ServerToken = token, SessionId = session } }); } catch (Exception e) { Log.Error(e, "Error in AUTH/START"); return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { Message = GlobalMessage.OP_INVALID, ReportTime = _time.SafeNow, } }); } }
public ActionResult DataUpload([FromQuery] string token, MilvanethProtocol data) { if (!_token.TryDecode(token, out var payload)) { return(StatusCode(401)); } if (!(data?.Context).Check() || !(data?.Data is PackedResult result) || !result.Check()) { return(StatusCode(400)); } try { if (payload.ValidTo < _time.UtcNow) { return(StatusCode(511)); } _auth.EnsureToken(payload, TokenPurpose.AccessToken, GlobalOperation.DATA_UPLOAD, 0, out var account); // virtual deduction for token validation operation var karmaBefore = account.Karma; account.Karma -= 20; switch (result.Type) { case PackedResultType.Inventory: var inventory = (InventoryResult)result.Result; if (inventory.Context != data.Context.CharacterId) { break; } _repo.Character.Commit(account.AccountId, new CharacterData { CharacterId = inventory.Context, ServiceId = data.Context.ServiceId, AccountId = account.AccountId, Inventory = LZ4MessagePackSerializer.Serialize(data.Data) }, false); account.Karma += 20; break; case PackedResultType.InventoryNetwork: var inventoryNetwork = (InventoryResult)result.Result; _repo.Retainer.Commit(account.AccountId, new RetainerData { RetainerId = inventoryNetwork.Context, Character = data.Context.CharacterId, World = data.Context.World, Inventory = LZ4MessagePackSerializer.Serialize(data.Data) }, false); account.Karma += 20; break; case PackedResultType.Artisan: var artisan = (ArtisanResult)result.Result; _repo.Character.CommitRange(account.AccountId, artisan.ArtisanList.Select(x => new CharacterData { CharacterId = x.CharacterId, CharacterName = x.IsValid ? x.CharacterName : null, HomeWorld = x.IsValid && !x.FromMemory ? data.Context.World : 0, }), false); account.Karma += 20 + artisan.ArtisanList.Count(x => x.IsValid) * 10 + artisan.ArtisanList.Count(x => !x.IsValid) * 3; break; case PackedResultType.MarketHistory: var marketHistory = (MarketHistoryResult)result.Result; _repo.History.CommitRange(data.Context.CharacterId, marketHistory.HistoryItems.Select(x => x.ToDb(result.ReportTime, data.Context.World)), false); account.Karma += 20 + 30; break; case PackedResultType.MarketListing: var marketListing = (MarketListingResult)result.Result; var artisanFk = marketListing.ListingItems.Select(x => x.ArtisanId); var ownerFk = marketListing.ListingItems.Select(x => new { x.OwnerId, x.PlayerName }).ToImmutableList(); _repo.Character.CommitRange(account.AccountId, ownerFk.Where(x => x.OwnerId != 0).GroupBy(x => x.OwnerId).Select(y => { var x = y.First(); return(new CharacterData { CharacterId = x.OwnerId, HomeWorld = data.Context.World, CharacterName = x.PlayerName }); }), false); _repo.Character.CommitRange(account.AccountId, artisanFk.Where(x => x > 0 && ownerFk.All(y => y.OwnerId != x)).Distinct().Select(x => new CharacterData { CharacterId = x }), true); _repo.Retainer.CommitRange(account.AccountId, marketListing.ListingItems.GroupBy(x => x.RetainerId).Select(y => { var x = y.First(); return(new RetainerData { RetainerId = x.RetainerId, Character = x.OwnerId, RetainerName = x.RetainerName, Location = x.RetainerLocation, World = data.Context.World }); }), true); _repo.Listing.CommitRange(data.Context.CharacterId, marketListing.ListingItems.Select(x => x.ToDb(result.ReportTime, data.Context.World)), false); foreach (var x in marketListing.ListingItems.GroupBy(x => x.ItemId)) { _repo.Overview.Commit(data.Context.CharacterId, new OverviewData { ReportTime = result.ReportTime, World = data.Context.World, ItemId = x.Key, OpenListing = (short)x.Count(), }, false); } account.Karma += 20 + 100; break; case PackedResultType.MarketOverview: var marketOverview = (MarketOverviewResult)result.Result; _repo.Overview.CommitRange(data.Context.CharacterId, marketOverview.ResultItems.Select(x => x.ToDb(result.ReportTime, data.Context.World)), false); account.Karma += 20 + 30 + marketOverview.ResultItems.Count; break; case PackedResultType.RetainerHistory: // todo break; case PackedResultType.RetainerList: var retainerList = (RetainerInfoResult)result.Result; _repo.Retainer.CommitRange(account.AccountId, retainerList.RetainerInfo.Select(x => x.ToDb(data.Context.CharacterId, data.Context.World)), false); account.Karma += 20 + retainerList.RetainerInfo.Count * 4; break; case PackedResultType.RetainerUpdate: #region NotFinishedCode // todo // 目前有两种方案,直接更新和Copy-on-Update,前者的主要问题在于并发条件下Time-Bucket体系可能会出现问题,后者则在于性能开销,故目前暂不对此数据进行处理 //var retainerUpdate = (RetainerUpdateResult)result.Result; //foreach (var item in retainerUpdate.UpdateItems) //{ // var record = _context.ListingData // .Where(x => x.RetainerId == item.RetainerId && x.ContainerId == (short)item.ContainerId && x.SlotId == item.ContainerSlot) // .OrderByDescending(x => x.ReportTime).Take(1); // if (!record.Any()) // { // var retInfo = // _context.RetainerData.SingleOrDefault(x => x.RetainerId == item.RetainerId); // var record2 = _context.ListingData.Where(x => // x.World == data.Context.World && x.ItemId == item.ItemInfo.ItemId && // x.ReportTime <= result.ReportTime) // .OrderByDescending(x => x.ReportTime).Take(1); // _repo.Commit(data.Context.CharacterId, new ListingData // { // BucketId = record2.Any() ? record2.First().BucketId : Guid.Empty, // ReportTime = result.ReportTime, // World = data.Context.World, // ReporterId = data.Context.CharacterId, // ListingId = 0, // RetainerId = item.RetainerId, // OwnerId = data.Context.CharacterId, // ArtisanId = item.ItemInfo.ArtisanId, // UnitPrice = item.NewPrice, // TotalTax = 0, // Quantity = item.ItemInfo.Amount, // ItemId = item.ItemInfo.ItemId, // UpdateTime = Helper.DateTimeToUnixTimeStamp(result.ReportTime), // ContainerId = (short)item.ContainerId, // SlotId = (short)item.ContainerSlot, // Condition = (short)item.ItemInfo.Durability, // SpiritBond = (short)item.ItemInfo.SpiritBond, // Materia1 = item.ItemInfo.Materia1, // Materia2 = item.ItemInfo.Materia2, // Materia3 = item.ItemInfo.Materia3, // Materia4 = item.ItemInfo.Materia4, // Materia5 = item.ItemInfo.Materia5, // RetainerName = retInfo?.RetainerName, // PlayerName = item.ContainerId == InventoryContainerId.HIRE_LISTING ? null : _context.CharacterData.SingleOrDefault(x => x.CharacterId == data.Context.CharacterId)?.CharacterName, // IsHq = item.ItemInfo.IsHq, // MateriaCount = 0, //todo // OnMannequin = item.ContainerId != InventoryContainerId.HIRE_LISTING, // RetainerLoc = 0,//todo // DyeId = item.ItemInfo.DyeId // }, true); // account.Karma += 25; // continue; // } // var recordEntity = record.First(); // if (recordEntity.ReportTime >= result.ReportTime) // { // account.Karma -= 25; // continue; // } // recordEntity. //} //account.Karma += 20 + retainerUpdate.UpdateItems.Count * 25; #endregion account.Karma += 20; break; case PackedResultType.Status: var status = (StatusResult)result.Result; if (status.CharacterId != data.Context.CharacterId) { break; } if (account.PlayedCharacter == null) { account.PlayedCharacter = new long[0]; } if (!account.PlayedCharacter.Contains(status.CharacterId)) { var temp = new long[account.PlayedCharacter.Length + 1]; Array.Copy(account.PlayedCharacter, temp, account.PlayedCharacter.Length); temp[temp.Length - 1] = status.CharacterId; account.PlayedCharacter = temp; } _repo.Character.Commit(account.AccountId, new CharacterData { CharacterId = status.CharacterId, CharacterName = status.CharacterName, ServiceId = data.Context.ServiceId, AccountId = account.AccountId, HomeWorld = status.CharacterHomeWorld, JobLevels = status.LevelInfo.ToDb(), GilHold = status.CharaInfo.GilHold }, false); account.Karma += 20 + 40; break; case PackedResultType.LobbyService: var lobbyService = (LobbyServiceResult)result.Result; if (lobbyService.ServiceId != data.Context.ServiceId) { break; } if (account.RelatedService == null) { account.RelatedService = new long[0]; } if (!account.RelatedService.Contains(lobbyService.ServiceId)) { var temp = new long[account.RelatedService.Length + 1]; Array.Copy(account.RelatedService, temp, account.RelatedService.Length); temp[temp.Length - 1] = lobbyService.ServiceId; account.RelatedService = temp; } account.Karma += 20; break; case PackedResultType.LobbyCharacter: var lobbyCharacter = (LobbyCharacterResult)result.Result; if (!DataChecker.CheckOnlineCharacterBinding(data.Context.ServiceId, lobbyCharacter.CharacterItems)) { #warning api availability is not checked. account.Karma -= 180; break; } if (account.RelatedService == null) { account.RelatedService = new long[0]; } if (!account.RelatedService.Contains(data.Context.ServiceId)) { var temp = new long[account.RelatedService.Length + 1]; Array.Copy(account.RelatedService, temp, account.RelatedService.Length); temp[temp.Length - 1] = data.Context.ServiceId; account.RelatedService = temp; } _repo.Character.CommitRange(account.AccountId, lobbyCharacter.CharacterItems.Select(x => x.ToDb(data.Context.ServiceId)), false); account.Karma += 20 + 20 * lobbyCharacter.CharacterItems.Count; break; default: // do nothing break; } _context.KarmaLog.Add(new KarmaLog { ReportTime = _time.UtcNow, AccountId = account.AccountId, Reason = GlobalOperation.DATA_UPLOAD + (int)result.Type, Before = karmaBefore, After = account.Karma }); _context.AccountData.Update(account); _context.SaveChanges(); return(StatusCode(200)); } catch (Exception e) { Log.Error(e, "Error in DATA/UPLOAD"); return(StatusCode(500)); } }
public IEnumerable <CharacterData> CommitRange(long accountId, IEnumerable <CharacterData> data, bool saveChange) { if (data == null) { throw new InvalidOperationException(); } var snapshot = data.ToImmutableList(); if (snapshot.Any(x => x.CharacterId <= 0)) { throw new InvalidOperationException(); } var count = snapshot.Count; if (count <= 0) { return(null); } if (count == 1) { return new[] { Commit(accountId, snapshot.Single(), saveChange) } } ; if (count >= 2) { var search = snapshot.Select(x => x.CharacterId); var source = _context.CharacterData.Where(x => search.Contains(x.CharacterId)); var sourceMirror = source.AsNoTracking().ToImmutableList(); var remain = snapshot.Where(x => sourceMirror.All(y => y.CharacterId != x.CharacterId)); //var remain = snapshot.Where(x => source.AsNoTracking().All(y => y.CharacterId != x.CharacterId)); foreach (var entity in source) { var delta = snapshot.Single(x => x.CharacterId == entity.CharacterId); if (delta.CharacterName != null && delta.CharacterName != entity.CharacterName) { _context.DataLog.Add(new DataLog { FromValue = entity.CharacterName, ToValue = delta.CharacterName, Operator = accountId, RecordId = entity.CharacterId, TableColumn = GlobalOperation.COLUMN_CHARACTER_NAME, ReportTime = _time.UtcNow }); entity.CharacterName = delta.CharacterName; } if (delta.AccountId != 0 && delta.AccountId != entity.AccountId) { _context.DataLog.Add(new DataLog { FromValue = entity.AccountId.ToString(), ToValue = delta.AccountId.ToString(), Operator = accountId, RecordId = entity.CharacterId, TableColumn = GlobalOperation.COLUMN_CHARACTER_ACCOUNT, ReportTime = _time.UtcNow }); entity.AccountId = delta.AccountId; } if (delta.ServiceId != 0 && delta.ServiceId != entity.ServiceId) { _context.DataLog.Add(new DataLog { FromValue = entity.ServiceId.ToString(), ToValue = delta.ServiceId.ToString(), Operator = accountId, RecordId = entity.CharacterId, TableColumn = GlobalOperation.COLUMN_CHARACTER_SERVICE, ReportTime = _time.UtcNow }); entity.ServiceId = delta.ServiceId; } if (delta.HomeWorld != 0 && delta.HomeWorld != entity.HomeWorld) { _context.DataLog.Add(new DataLog { FromValue = entity.HomeWorld.ToString(), ToValue = delta.HomeWorld.ToString(), Operator = accountId, RecordId = entity.CharacterId, TableColumn = GlobalOperation.COLUMN_CHARACTER_WORLD, ReportTime = _time.UtcNow }); entity.HomeWorld = delta.HomeWorld; } if (delta.RetainerList != null && !delta.RetainerList.SequenceEqual(entity.RetainerList)) { entity.RetainerList = delta.RetainerList; } if (delta.JobLevels != null && !delta.JobLevels.SequenceEqual(entity.JobLevels)) { entity.JobLevels = delta.JobLevels; } if (delta.Inventory != null) { entity.Inventory = delta.Inventory; } if (delta.GilHold != null) { entity.GilHold = delta.GilHold; } _context.CharacterData.Update(entity); } if (remain.Any()) { _context.CharacterData.AddRange(remain); _context.DataLog.Add(new DataLog { FromValue = null, ToValue = "INITIAL COMMIT MULTIPLE", Operator = accountId, RecordId = 0, TableColumn = GlobalOperation.COLUMN_CHARACTER_NEW, ReportTime = _time.UtcNow }); } if (saveChange) { _context.SaveChanges(); } return(source.Concat(remain)); } return(null); }
public ActionResult <MilvanethProtocol> AccountCreate(MilvanethProtocol data) { if (!(data?.Data is RegisterForm form) || !form.Check()) { return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { Message = GlobalMessage.DATA_INVALID_INPUT, ReportTime = _time.SafeNow, } }); } if (!_pow.Verify(form.ProofOfWork)) { return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { Message = GlobalMessage.RATE_POW_REQUIRED, ProofOfWork = _pow.Generate((byte)Math.Max(GlobalConfig.POW_SENSITIVE_OPERATION, _pow.Difficulty)), ReportTime = _time.SafeNow, } }); } try { if (_context.AccountData.Any(x => x.AccountName == form.Username)) { return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { Message = GlobalMessage.DATA_USERNAME_OCCUPIED, ReportTime = _time.SafeNow, } }); } //var accountData = _context.AccountData.CreateProxy(); var accountData = new AccountData(); { accountData.AccountName = form.Username; accountData.DisplayName = form.DisplayName; accountData.Email = form.Email; accountData.EmailConfirmed = false; accountData.Salt = form.Salt; accountData.Verifier = form.Verifier; accountData.GroupParam = (short)form.GroupParam; accountData.RegisterService = form.Service.ServiceId; accountData.RelatedService = new long[] { form.Service.ServiceId }; accountData.PlayedCharacter = null; accountData.Trace = form.Trace; accountData.Karma = 0; accountData.PrivilegeLevelNavigation = _userPrivilege; accountData.SuspendUntil = null; accountData.PasswordRetry = 0; accountData.LastRetry = DateTime.UtcNow; } _context.AccountData.Add(accountData); _context.SaveChanges(); _repo.Character.CommitRange(accountData.AccountId, form.Character.CharacterItems.Select(x => x.ToDb(accountData.RegisterService)), true); _auth.EnsureAccount(accountData, new PrivilegeConfig { Login = true }, GlobalOperation.ACCOUNT_CREATE, GlobalConfig.USER_INITIAL_KARMA, "Registered new account via account/create", _accessor.GetIp()); var session = _srp.DoServerResponse(accountData.AccountId, accountData.GroupParam, accountData.Verifier, out var token); return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { GroupParam = accountData.GroupParam, Message = GlobalMessage.OK_SUCCESS, ReportTime = _time.SafeNow, Salt = accountData.Salt, ServerToken = token, SessionId = session } }); } catch (Exception e) { Log.Error(e, "Error in ACCOUNT/CREATE"); return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { Message = GlobalMessage.OP_INVALID, ReportTime = _time.SafeNow, } }); } }