implement client side session mangement

This commit is contained in:
Mikhail
2024-09-21 14:45:08 -04:00
parent 3f32e569d9
commit 7ac711b027
6 changed files with 134 additions and 93 deletions

View File

@@ -2,6 +2,8 @@
using EpinelPS.StaticInfo;
using EpinelPS.Utils;
using Newtonsoft.Json;
using Paseto.Builder;
using Paseto;
namespace EpinelPS.Database
{
@@ -121,7 +123,7 @@ namespace EpinelPS.Database
public NetJukeboxLocation Location;
public NetJukeboxBgmType Type;
public int TableId;
}
public class User
{
@@ -138,7 +140,7 @@ namespace EpinelPS.Database
public bool ProfileIconIsPrism = false;
public int ProfileFrame = 25;
public bool IsAdmin = false;
public bool sickpulls = false;
public bool sickpulls = false;
public bool IsBanned = false;
public DateTime BanStart;
public DateTime BanEnd;
@@ -168,7 +170,7 @@ namespace EpinelPS.Database
public NetWallpaperJukeboxFavorite[] WallpaperFavoriteList = [];
public NetWallpaperPlaylist[] WallpaperPlaylistList = [];
public NetWallpaperJukebox[] WallpaperJukeboxList = [];
public Dictionary<int, NetUserTeamData> UserTeams = new Dictionary<int, NetUserTeamData>();
public Dictionary<int, bool> MainQuestData = new();
@@ -188,7 +190,7 @@ namespace EpinelPS.Database
public Dictionary<int, int> TowerProgress = new Dictionary<int, int>();
public JukeBoxSetting LobbyMusic = new() { Location = NetJukeboxLocation.NetJukeboxLocationLobby, TableId = 2, Type = NetJukeboxBgmType.NetJukeboxBgmTypeJukeboxTableId};
public JukeBoxSetting LobbyMusic = new() { Location = NetJukeboxLocation.NetJukeboxLocationLobby, TableId = 2, Type = NetJukeboxBgmType.NetJukeboxBgmTypeJukeboxTableId };
public JukeBoxSetting CommanderMusic = new() { Location = NetJukeboxLocation.NetJukeboxLocationCommanderRoom, TableId = 5, Type = NetJukeboxBgmType.NetJukeboxBgmTypeJukeboxTableId };
// Event data
@@ -343,8 +345,9 @@ namespace EpinelPS.Database
public List<AccessToken> LauncherAccessTokens = [];
public Dictionary<string, GameClientInfo> GameClientTokens = [];
public string ServerName = "<color=\"green\">Private Server</color>";
public byte[] LauncherTokenKey = [];
public byte[] EncryptionTokenKey = [];
}
internal class JsonDb
{
@@ -423,6 +426,24 @@ namespace EpinelPS.Database
}
Console.WriteLine("Database update completed");
}
if (Instance.LauncherTokenKey.Length == 0)
{
Console.WriteLine("Launcher token key is null, generating new key");
var pasetoKey = new PasetoBuilder().Use(ProtocolVersion.V4, Purpose.Local)
.GenerateSymmetricKey();
Instance.LauncherTokenKey = pasetoKey.Key.ToArray();
}
if (Instance.EncryptionTokenKey.Length == 0)
{
Console.WriteLine("EncryptionTokenKey is null, generating new key");
var pasetoKey = new PasetoBuilder().Use(ProtocolVersion.V4, Purpose.Local)
.GenerateSymmetricKey();
Instance.EncryptionTokenKey = pasetoKey.Key.ToArray();
}
Save();
ValidateDb();
@@ -437,9 +458,9 @@ namespace EpinelPS.Database
private static void ValidateDb()
{
// check if character level is valid
foreach (var item in Instance.Users)
foreach (var user in Instance.Users)
{
foreach (var c in item.Characters)
foreach (var c in user.Characters)
{
if (c.Level > 1000)
{
@@ -447,6 +468,26 @@ namespace EpinelPS.Database
c.Level = 1000;
}
}
// Check if RepresentationTeamData exists and has slots
if (user.RepresentationTeamData != null && user.RepresentationTeamData.Slots != null)
{
// Iterate through RepresentationTeamData slots
foreach (var slot in user.RepresentationTeamData.Slots)
{
// Find the character in user's character list that matches the slot's Tid
var correspondingCharacter = user.Characters.FirstOrDefault(c => c.Tid == slot.Tid);
if (correspondingCharacter != null)
{
// Update the CSN value if it differs
if (slot.Csn != correspondingCharacter.Csn)
{
slot.Csn = correspondingCharacter.Csn;
}
}
}
}
}
}
@@ -461,44 +502,44 @@ namespace EpinelPS.Database
File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + "/db.json", JsonConvert.SerializeObject(Instance, Formatting.Indented));
}
}
public static int CurrentJukeboxBgm(int position)
{
var activeJukeboxBgm = new List<int>();
//important first position holds lobby bgm id and second commanders room bgm id
foreach (var user in Instance.Users)
{
if (user.JukeboxBgm == null || user.JukeboxBgm.Count == 0)
{
// this if statemet only exists becaus some weird black magic copies default value over and over
//in the file when its set in public List<int> JukeboxBgm = new List<int>();
//delete when or if it gets fixed
user.JukeboxBgm = new List<int> { 2,5 };
}
public static int CurrentJukeboxBgm(int position)
{
var activeJukeboxBgm = new List<int>();
//important first position holds lobby bgm id and second commanders room bgm id
foreach (var user in Instance.Users)
{
if (user.JukeboxBgm == null || user.JukeboxBgm.Count == 0)
{
// this if statemet only exists becaus some weird black magic copies default value over and over
//in the file when its set in public List<int> JukeboxBgm = new List<int>();
//delete when or if it gets fixed
activeJukeboxBgm.AddRange(user.JukeboxBgm);
}
user.JukeboxBgm = new List<int> { 2, 5 };
}
if (activeJukeboxBgm.Count == 0)
{
return 8995001;
}
activeJukeboxBgm.AddRange(user.JukeboxBgm);
}
position = (position == 2 && activeJukeboxBgm.Count > 1) ? 2 : 1;
return activeJukeboxBgm[position - 1];
}
if (activeJukeboxBgm.Count == 0)
{
return 8995001;
}
public static bool IsSickPulls(User selectedUser)
{
if (selectedUser != null)
{
return selectedUser.sickpulls;
}
else
{
throw new Exception($"User with ID {selectedUser.ID} not found");
}
}
position = (position == 2 && activeJukeboxBgm.Count > 1) ? 2 : 1;
return activeJukeboxBgm[position - 1];
}
public static bool IsSickPulls(User selectedUser)
{
if (selectedUser != null)
{
return selectedUser.sickpulls;
}
else
{
throw new Exception($"User with ID {selectedUser.ID} not found");
}
}
}
}

View File

@@ -2,6 +2,9 @@
using EpinelPS.Database;
using EpinelPS.Utils;
using Google.Protobuf;
using Paseto.Builder;
using Paseto;
using Newtonsoft.Json;
namespace EpinelPS.LobbyServer
{
@@ -64,7 +67,7 @@ namespace EpinelPS.LobbyServer
/// </summary>
/// <param name="publicKey"></param>
/// <returns></returns>
public static GameClientInfo GenGameClientTok(ByteString publicKey, string authToken)
public static GameClientInfo GenGameClientTok(ByteString publicKey, ulong userid)
{
var token = Rng.RandomString(381);
@@ -75,25 +78,23 @@ namespace EpinelPS.LobbyServer
info.Keys = box;
info.ClientAuthToken = token;
// look up user id
foreach (var user in JsonDb.Instance.LauncherAccessTokens)
{
if (user.Token == authToken)
{
info.UserId = user.UserID;
}
}
if (info.UserId == 0)
if (userid == 0)
throw new Exception("expected user account");
JsonDb.Instance.GameClientTokens.Add(token, info);
JsonDb.Save();
info.UserId = userid;
return info;
}
public static GameClientInfo? GetInfo(string token)
{
return JsonDb.Instance.GameClientTokens[token];
var encryptionToken = new PasetoBuilder().Use(ProtocolVersion.V4, Purpose.Local)
.WithKey(JsonDb.Instance.LauncherTokenKey, Encryption.SymmetricKey)
.Decode(token, new PasetoTokenValidationParameters() { ValidateLifetime = true });
var p = ((System.Text.Json.JsonElement)encryptionToken.Paseto.Payload["data"]).GetString();
return JsonConvert.DeserializeObject<GameClientInfo>(p);
}
public static void Init()

View File

@@ -1,6 +1,11 @@
using Google.Protobuf;
using EpinelPS.Database;
using EpinelPS.Utils;
using Newtonsoft.Json;
using Paseto.Builder;
using Paseto;
using System.Security.Cryptography;
using Newtonsoft.Json.Linq;
namespace EpinelPS.LobbyServer
{
@@ -41,13 +46,13 @@ namespace EpinelPS.LobbyServer
public async Task HandleAsync(string authToken)
{
this.UsedAuthToken = authToken;
foreach (var item in JsonDb.Instance.GameClientTokens)
{
if (item.Key == authToken)
{
UserId = item.Value.UserId;
}
}
var encryptionToken = new PasetoBuilder().Use(ProtocolVersion.V4, Purpose.Local)
.WithKey(JsonDb.Instance.LauncherTokenKey, Encryption.SymmetricKey)
.Decode(authToken, new PasetoTokenValidationParameters() { ValidateLifetime = true});
UserId = ((System.Text.Json.JsonElement)encryptionToken.Paseto.Payload["userid"]).GetUInt64();
if (UserId == 0) throw new Exception("403");
await HandleAsync();
}

View File

@@ -10,7 +10,7 @@ namespace EpinelPS.LobbyServer.Msgs.Auth
{
var req = await ReadData<ReqLogout>();
JsonDb.Instance.GameClientTokens.Remove(UsedAuthToken);
// TODO remove UsedAuthToken
await WriteDataAsync(new ResLogout());
}

View File

@@ -2,6 +2,10 @@
using EpinelPS.Utils;
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Paseto.Builder;
using Paseto;
using Newtonsoft.Json;
namespace EpinelPS.LobbyServer.Msgs.Auth
{
@@ -22,17 +26,33 @@ namespace EpinelPS.LobbyServer.Msgs.Auth
}
}
if (UserId == 0) throw new BadHttpRequestException("unknown auth token", 403);
var user = GetUser();
var rsp = LobbyHandler.GenGameClientTok(req.ClientPublicKey, UserId);
var token = new PasetoBuilder().Use(ProtocolVersion.V4, Purpose.Local)
.WithKey(JsonDb.Instance.LauncherTokenKey, Encryption.SymmetricKey)
.AddClaim("userid", UserId)
.IssuedAt(DateTime.UtcNow)
.Expiration(DateTime.UtcNow.AddDays(2))
.Encode();
var encryptionToken = new PasetoBuilder().Use(ProtocolVersion.V4, Purpose.Local)
.WithKey(JsonDb.Instance.LauncherTokenKey, Encryption.SymmetricKey)
.AddClaim("data", JsonConvert.SerializeObject(rsp))
.IssuedAt(DateTime.UtcNow)
.Expiration(DateTime.UtcNow.AddDays(2))
.Encode();
var response = new ResEnterServer();
var rsp = LobbyHandler.GenGameClientTok(req.ClientPublicKey, req.AuthToken);
response.GameClientToken = rsp.ClientAuthToken;
response.GameClientToken = token;
response.FeatureDataInfo = new NetFeatureDataInfo() { UseFeatureData = true };
response.Identifier = new NetLegacyUserIdentifier() { Server = 21769, Usn = (long)user.ID };
response.Identifier = new NetLegacyUserIdentifier() { Server = 1000, Usn = (long)user.ID };
response.ShouldRestartAfter = Duration.FromTimeSpan(TimeSpan.FromSeconds(86400));
response.EncryptionToken = ByteString.CopyFromUtf8(rsp.ClientAuthToken);
response.EncryptionToken = ByteString.CopyFromUtf8(encryptionToken);
await WriteDataAsync(response);
}
}

View File

@@ -29,32 +29,6 @@ namespace EpinelPS
Console.WriteLine("Initialize handlers");
LobbyHandler.Init();
foreach (var user in JsonDb.Instance.Users)
{
// Check if RepresentationTeamData exists and has slots
if (user.RepresentationTeamData != null && user.RepresentationTeamData.Slots != null)
{
// Iterate through RepresentationTeamData slots
foreach (var slot in user.RepresentationTeamData.Slots)
{
// Find the character in user's character list that matches the slot's Tid
var correspondingCharacter = user.Characters.FirstOrDefault(c => c.Tid == slot.Tid);
if (correspondingCharacter != null)
{
// Update the CSN value if it differs
if (slot.Csn != correspondingCharacter.Csn)
{
slot.Csn = correspondingCharacter.Csn;
}
}
}
}
}
// Save the updated data
JsonDb.Save();
Console.WriteLine("Starting ASP.NET core on ports 80/443");
new Thread(() =>
{