Testing more garbage encryption
This commit is contained in:
@@ -58,6 +58,7 @@ public class DCGEDbContext : DbContext
|
|||||||
IEnumerable<string> pendingMigrations = Database.GetPendingMigrations();
|
IEnumerable<string> pendingMigrations = Database.GetPendingMigrations();
|
||||||
if (!pendingMigrations.Any())
|
if (!pendingMigrations.Any())
|
||||||
{
|
{
|
||||||
|
_logger.LogDebug("No pending migrations found, continuing.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
SVSim.Database/Enums/SocialAccountType.cs
Normal file
12
SVSim.Database/Enums/SocialAccountType.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace SVSim.Database.Enums;
|
||||||
|
|
||||||
|
public enum SocialAccountType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
GooglePlay,
|
||||||
|
GameCenter,
|
||||||
|
Facebook,
|
||||||
|
Dmm,
|
||||||
|
Steam,
|
||||||
|
AppleId
|
||||||
|
}
|
||||||
171
SVSim.Database/Migrations/20240907191709_Initial.Designer.cs
generated
Normal file
171
SVSim.Database/Migrations/20240907191709_Initial.Designer.cs
generated
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using SVSim.Database;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace SVSim.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(SVSimDbContext))]
|
||||||
|
[Migration("20240907191709_Initial")]
|
||||||
|
partial class Initial
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "8.0.8");
|
||||||
|
|
||||||
|
modelBuilder.Entity("DCGEngine.Database.Models.CardEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("Attack")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateUpdated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("Defense")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Discriminator")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(21)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("InternalName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("PrimaryResourceCost")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long?>("ShadowverseDeckEntryId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ShadowverseDeckEntryId");
|
||||||
|
|
||||||
|
b.ToTable("CardEntry");
|
||||||
|
|
||||||
|
b.HasDiscriminator().HasValue("CardEntry");
|
||||||
|
|
||||||
|
b.UseTphMappingStrategy();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SVSim.Database.Models.ShadowverseDeckEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateUpdated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("InternalName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ShadowverseDeckEntry");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SVSim.Database.Models.SocialAccountConnection", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("AccountId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("AccountType")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateUpdated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("ViewerId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ViewerId");
|
||||||
|
|
||||||
|
b.ToTable("SocialAccountConnection");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SVSim.Database.Models.Viewer", b =>
|
||||||
|
{
|
||||||
|
b.Property<ulong>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateUpdated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("ShortUdid")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Viewer");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SVSim.Database.Models.ShadowverseCardEntry", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("DCGEngine.Database.Models.CardEntry");
|
||||||
|
|
||||||
|
b.HasDiscriminator().HasValue("ShadowverseCardEntry");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DCGEngine.Database.Models.CardEntry", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SVSim.Database.Models.ShadowverseDeckEntry", null)
|
||||||
|
.WithMany("Cards")
|
||||||
|
.HasForeignKey("ShadowverseDeckEntryId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SVSim.Database.Models.SocialAccountConnection", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SVSim.Database.Models.Viewer", "Viewer")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ViewerId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Viewer");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SVSim.Database.Models.ShadowverseDeckEntry", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Cards");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
118
SVSim.Database/Migrations/20240907191709_Initial.cs
Normal file
118
SVSim.Database/Migrations/20240907191709_Initial.cs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace SVSim.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Initial : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ShadowverseDeckEntry",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
DateCreated = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||||
|
DateUpdated = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||||
|
InternalName = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ShadowverseDeckEntry", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Viewer",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<ulong>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
DisplayName = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
ShortUdid = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
DateCreated = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||||
|
DateUpdated = table.Column<DateTime>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Viewer", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "CardEntry",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
InternalName = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Attack = table.Column<int>(type: "INTEGER", nullable: true),
|
||||||
|
Defense = table.Column<int>(type: "INTEGER", nullable: true),
|
||||||
|
PrimaryResourceCost = table.Column<int>(type: "INTEGER", nullable: true),
|
||||||
|
Discriminator = table.Column<string>(type: "TEXT", maxLength: 21, nullable: false),
|
||||||
|
ShadowverseDeckEntryId = table.Column<long>(type: "INTEGER", nullable: true),
|
||||||
|
DateCreated = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||||
|
DateUpdated = table.Column<DateTime>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_CardEntry", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_CardEntry_ShadowverseDeckEntry_ShadowverseDeckEntryId",
|
||||||
|
column: x => x.ShadowverseDeckEntryId,
|
||||||
|
principalTable: "ShadowverseDeckEntry",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SocialAccountConnection",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||||
|
AccountType = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
AccountId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
ViewerId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
DateCreated = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||||
|
DateUpdated = table.Column<DateTime>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SocialAccountConnection", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_SocialAccountConnection_Viewer_ViewerId",
|
||||||
|
column: x => x.ViewerId,
|
||||||
|
principalTable: "Viewer",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_CardEntry_ShadowverseDeckEntryId",
|
||||||
|
table: "CardEntry",
|
||||||
|
column: "ShadowverseDeckEntryId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_SocialAccountConnection_ViewerId",
|
||||||
|
table: "SocialAccountConnection",
|
||||||
|
column: "ViewerId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "CardEntry");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SocialAccountConnection");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ShadowverseDeckEntry");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Viewer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
168
SVSim.Database/Migrations/SVSimDbContextModelSnapshot.cs
Normal file
168
SVSim.Database/Migrations/SVSimDbContextModelSnapshot.cs
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using SVSim.Database;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace SVSim.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(SVSimDbContext))]
|
||||||
|
partial class SVSimDbContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "8.0.8");
|
||||||
|
|
||||||
|
modelBuilder.Entity("DCGEngine.Database.Models.CardEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("Attack")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateUpdated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("Defense")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Discriminator")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(21)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("InternalName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("PrimaryResourceCost")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long?>("ShadowverseDeckEntryId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ShadowverseDeckEntryId");
|
||||||
|
|
||||||
|
b.ToTable("CardEntry");
|
||||||
|
|
||||||
|
b.HasDiscriminator().HasValue("CardEntry");
|
||||||
|
|
||||||
|
b.UseTphMappingStrategy();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SVSim.Database.Models.ShadowverseDeckEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateUpdated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("InternalName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ShadowverseDeckEntry");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SVSim.Database.Models.SocialAccountConnection", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("AccountId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("AccountType")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateUpdated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("ViewerId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ViewerId");
|
||||||
|
|
||||||
|
b.ToTable("SocialAccountConnection");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SVSim.Database.Models.Viewer", b =>
|
||||||
|
{
|
||||||
|
b.Property<ulong>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateUpdated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("ShortUdid")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Viewer");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SVSim.Database.Models.ShadowverseCardEntry", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("DCGEngine.Database.Models.CardEntry");
|
||||||
|
|
||||||
|
b.HasDiscriminator().HasValue("ShadowverseCardEntry");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DCGEngine.Database.Models.CardEntry", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SVSim.Database.Models.ShadowverseDeckEntry", null)
|
||||||
|
.WithMany("Cards")
|
||||||
|
.HasForeignKey("ShadowverseDeckEntryId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SVSim.Database.Models.SocialAccountConnection", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("SVSim.Database.Models.Viewer", "Viewer")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ViewerId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Viewer");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SVSim.Database.Models.ShadowverseDeckEntry", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Cards");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
SVSim.Database/Models/SocialAccountConnection.cs
Normal file
25
SVSim.Database/Models/SocialAccountConnection.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using DCGEngine.Database.Models;
|
||||||
|
using SVSim.Database.Enums;
|
||||||
|
|
||||||
|
namespace SVSim.Database.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A connection between a social account (ie facebook) and a viewer.
|
||||||
|
/// </summary>
|
||||||
|
public class SocialAccountConnection : BaseEntity<Guid>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The type of the social account.
|
||||||
|
/// </summary>
|
||||||
|
public SocialAccountType AccountType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The identifier of the social account.
|
||||||
|
/// </summary>
|
||||||
|
public ulong AccountId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The viewer connected.
|
||||||
|
/// </summary>
|
||||||
|
public Viewer Viewer { get; set; }
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using DCGEngine.Database.Models;
|
|
||||||
|
|
||||||
namespace SVSim.Database.Models;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A user within the game system.
|
|
||||||
/// </summary>
|
|
||||||
public class User : BaseEntity<long>
|
|
||||||
{
|
|
||||||
public string ViewerId { get; set; }
|
|
||||||
public ulong SteamId { get; set; }
|
|
||||||
public string DisplayName { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
19
SVSim.Database/Models/Viewer.cs
Normal file
19
SVSim.Database/Models/Viewer.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using DCGEngine.Database.Models;
|
||||||
|
|
||||||
|
namespace SVSim.Database.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A user within the game system.
|
||||||
|
/// </summary>
|
||||||
|
public class Viewer : BaseEntity<ulong>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This user's name displayed in game.
|
||||||
|
/// </summary>
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This user's short identifier.
|
||||||
|
/// </summary>
|
||||||
|
public ulong ShortUdid { get; set; }
|
||||||
|
}
|
||||||
8
SVSim.Database/Repositories/Viewer/IViewerRepository.cs
Normal file
8
SVSim.Database/Repositories/Viewer/IViewerRepository.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using SVSim.Database.Enums;
|
||||||
|
|
||||||
|
namespace SVSim.Database.Repositories.Viewer;
|
||||||
|
|
||||||
|
public interface IViewerRepository
|
||||||
|
{
|
||||||
|
Task<Models.Viewer?> GetViewerBySocialConnection(SocialAccountType accountType, ulong socialId);
|
||||||
|
}
|
||||||
24
SVSim.Database/Repositories/Viewer/ViewerRepository.cs
Normal file
24
SVSim.Database/Repositories/Viewer/ViewerRepository.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using SVSim.Database.Enums;
|
||||||
|
using SVSim.Database.Models;
|
||||||
|
|
||||||
|
namespace SVSim.Database.Repositories.Viewer;
|
||||||
|
|
||||||
|
public class ViewerRepository : IViewerRepository
|
||||||
|
{
|
||||||
|
protected readonly SVSimDbContext _dbContext;
|
||||||
|
|
||||||
|
public ViewerRepository(SVSimDbContext dbContext)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Models.Viewer?> GetViewerBySocialConnection(SocialAccountType accountType, ulong socialId)
|
||||||
|
{
|
||||||
|
return _dbContext.Set<SocialAccountConnection>()
|
||||||
|
.AsNoTracking()
|
||||||
|
.Include(sac => sac.Viewer)
|
||||||
|
.FirstOrDefault(sac => sac.AccountType == accountType && sac.AccountId == socialId)
|
||||||
|
?.Viewer;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
SVSim.EmulatedEntrypoint/Constants/NetworkConstants.cs
Normal file
8
SVSim.EmulatedEntrypoint/Constants/NetworkConstants.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace SVSim.EmulatedEntrypoint.Constants;
|
||||||
|
|
||||||
|
public static class NetworkConstants
|
||||||
|
{
|
||||||
|
public const string UdidHeaderName = "UDID";
|
||||||
|
public const string SessionIdHeaderName = "SID";
|
||||||
|
public const string ShortUdidHeaderName = "SHORT_UDID";
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace SVSim.EmulatedEntrypoint.Constants;
|
||||||
|
|
||||||
|
public static class ShadowverseClaimTypes
|
||||||
|
{
|
||||||
|
public const string ShortUdidClaim = "ShortUdid";
|
||||||
|
public const string ViewerIdClaim = "ViewerId";
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Buffers.Text;
|
using System.Buffers.Text;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using MessagePack;
|
using MessagePack;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -22,6 +23,7 @@ namespace SVSim.EmulatedEntrypoint.Controllers
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
[HttpPost("special_title")]
|
[HttpPost("special_title")]
|
||||||
public async Task<DataWrapper<SpecialTitleCheckResponse>> SpecialTitleCheck(SpecialTitleCheckRequest request)
|
public async Task<DataWrapper<SpecialTitleCheckResponse>> SpecialTitleCheck(SpecialTitleCheckRequest request)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using SVSim.EmulatedEntrypoint.Security;
|
using SVSim.EmulatedEntrypoint.Security;
|
||||||
|
using SVSim.EmulatedEntrypoint.Security.SteamSessionAuthentication;
|
||||||
|
|
||||||
namespace SVSim.EmulatedEntrypoint.Controllers
|
namespace SVSim.EmulatedEntrypoint.Controllers
|
||||||
{
|
{
|
||||||
@@ -9,11 +11,8 @@ namespace SVSim.EmulatedEntrypoint.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
|
[Authorize(AuthenticationSchemes = SteamAuthenticationConstants.SchemeName)]
|
||||||
public abstract class SVSimController : ControllerBase
|
public abstract class SVSimController : ControllerBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Returns the UdId of the user making the request. Can be null or empty, as only certain requests will send it. Known requests to send this value are: SignUp, CheckSpecialTitle, CheckiCloudUser, MigrateiCloudUser
|
|
||||||
/// </summary>
|
|
||||||
public string? UdId => Encryption.Decode(Request.Headers["UDID"]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using SVSim.EmulatedEntrypoint.Constants;
|
||||||
|
using SVSim.EmulatedEntrypoint.Security;
|
||||||
|
using SVSim.EmulatedEntrypoint.Services;
|
||||||
|
|
||||||
|
namespace SVSim.EmulatedEntrypoint.Middlewares;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps an incoming request's session id to a udid if both are present.
|
||||||
|
/// </summary>
|
||||||
|
public class SessionidMappingMiddleware : IMiddleware
|
||||||
|
{
|
||||||
|
private readonly ShadowverseSessionService _shadowverseSessionService;
|
||||||
|
|
||||||
|
public SessionidMappingMiddleware(ShadowverseSessionService shadowverseSessionService)
|
||||||
|
{
|
||||||
|
_shadowverseSessionService = shadowverseSessionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
|
||||||
|
{
|
||||||
|
bool hasSessionId = context.Request.Headers.TryGetValue(NetworkConstants.UdidHeaderName, out var udid);
|
||||||
|
bool hasUdid = context.Request.Headers.TryGetValue(NetworkConstants.SessionIdHeaderName, out var sid);
|
||||||
|
if (hasSessionId && hasUdid)
|
||||||
|
{
|
||||||
|
_shadowverseSessionService.StoreUdidForSessionId(sid.FirstOrDefault(), Guid.Parse(Encryption.Decode(udid.FirstOrDefault())));
|
||||||
|
}
|
||||||
|
|
||||||
|
await next.Invoke(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,9 @@ using Microsoft.AspNetCore.Mvc.Controllers;
|
|||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using SVSim.EmulatedEntrypoint.Constants;
|
||||||
using SVSim.EmulatedEntrypoint.Security;
|
using SVSim.EmulatedEntrypoint.Security;
|
||||||
|
using SVSim.EmulatedEntrypoint.Services;
|
||||||
|
|
||||||
namespace SVSim.EmulatedEntrypoint.Middlewares;
|
namespace SVSim.EmulatedEntrypoint.Middlewares;
|
||||||
|
|
||||||
@@ -14,10 +16,12 @@ namespace SVSim.EmulatedEntrypoint.Middlewares;
|
|||||||
public class ShadowverseTranslationMiddleware : IMiddleware
|
public class ShadowverseTranslationMiddleware : IMiddleware
|
||||||
{
|
{
|
||||||
private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;
|
private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;
|
||||||
|
private readonly ShadowverseSessionService _sessionService;
|
||||||
|
|
||||||
public ShadowverseTranslationMiddleware(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider)
|
public ShadowverseTranslationMiddleware(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider, ShadowverseSessionService sessionService)
|
||||||
{
|
{
|
||||||
_actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
|
_actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
|
||||||
|
_sessionService = sessionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
|
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
|
||||||
@@ -32,21 +36,33 @@ public class ShadowverseTranslationMiddleware : IMiddleware
|
|||||||
await next.Invoke(context);
|
await next.Invoke(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
using var requestBytesStream = new MemoryStream();
|
|
||||||
|
// Replace response body stream to re-access it.
|
||||||
using var tempResponseBody = new MemoryStream();
|
using var tempResponseBody = new MemoryStream();
|
||||||
var originalResponsebody = context.Response.Body;
|
var originalResponsebody = context.Response.Body;
|
||||||
context.Response.Body = tempResponseBody;
|
context.Response.Body = tempResponseBody;
|
||||||
|
|
||||||
|
// Pull out the request bytes into a stream
|
||||||
|
using var requestBytesStream = new MemoryStream();
|
||||||
await context.Request.Body.CopyToAsync(requestBytesStream);
|
await context.Request.Body.CopyToAsync(requestBytesStream);
|
||||||
byte[] requestBytes = requestBytesStream.ToArray();
|
byte[] requestBytes = requestBytesStream.ToArray();
|
||||||
// Decrypt incoming data. Placeholder.
|
|
||||||
requestBytes = Encryption.Decrypt(requestBytes, Encryption.Decode(context.Request.Headers["UDID"]));
|
// Get encryption values for this request
|
||||||
|
string sid = context.Request.Headers[NetworkConstants.SessionIdHeaderName];
|
||||||
|
string udid = _sessionService.GetUdidFromSessionId(sid).GetValueOrDefault().ToString();
|
||||||
|
|
||||||
|
// Decrypt incoming data.
|
||||||
|
requestBytes = Encryption.Decrypt(requestBytes, udid);
|
||||||
object? data = MessagePackSerializer.Deserialize(endpointDescriptor.Parameters.FirstOrDefault().ParameterType,
|
object? data = MessagePackSerializer.Deserialize(endpointDescriptor.Parameters.FirstOrDefault().ParameterType,
|
||||||
requestBytes);
|
requestBytes);
|
||||||
var json = JsonConvert.SerializeObject(data);
|
var json = JsonConvert.SerializeObject(data);
|
||||||
var newStream = new StringContent(json, Encoding.UTF8, "application/json");
|
var newStream = new StringContent(json, Encoding.UTF8, "application/json");
|
||||||
context.Request.Body = newStream.ReadAsStream();
|
context.Request.Body = newStream.ReadAsStream();
|
||||||
context.Request.Headers.ContentType = new StringValues("application/json");
|
context.Request.Headers.ContentType = new StringValues("application/json");
|
||||||
|
|
||||||
await next.Invoke(context);
|
await next.Invoke(context);
|
||||||
|
|
||||||
|
// Convert the response into a messagepack, encrypt it
|
||||||
var responseType = ((ControllerActionDescriptor)endpointDescriptor).MethodInfo.ReturnType;
|
var responseType = ((ControllerActionDescriptor)endpointDescriptor).MethodInfo.ReturnType;
|
||||||
if (responseType.IsGenericType && responseType.GetGenericTypeDefinition() == typeof(Task<>))
|
if (responseType.IsGenericType && responseType.GetGenericTypeDefinition() == typeof(Task<>))
|
||||||
{
|
{
|
||||||
@@ -58,7 +74,7 @@ public class ShadowverseTranslationMiddleware : IMiddleware
|
|||||||
var responseBytes = responseBytesStream.ToArray();
|
var responseBytes = responseBytesStream.ToArray();
|
||||||
var responseData = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(responseBytes), responseType);
|
var responseData = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(responseBytes), responseType);
|
||||||
var packedData = MessagePackSerializer.Serialize(responseType, responseData);
|
var packedData = MessagePackSerializer.Serialize(responseType, responseData);
|
||||||
packedData = Encryption.Encrypt(packedData, Encryption.Decode(context.Request.Headers["UDID"]));
|
packedData = Encryption.Encrypt(packedData, udid);
|
||||||
await originalResponsebody.WriteAsync(Encoding.UTF8.GetBytes(Convert.ToBase64String(packedData)));
|
await originalResponsebody.WriteAsync(Encoding.UTF8.GetBytes(Convert.ToBase64String(packedData)));
|
||||||
context.Response.Body = originalResponsebody;
|
context.Response.Body = originalResponsebody;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ using MessagePack;
|
|||||||
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Requests;
|
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Requests;
|
||||||
|
|
||||||
[MessagePackObject]
|
[MessagePackObject]
|
||||||
public abstract class BaseRequest
|
public class BaseRequest
|
||||||
{
|
{
|
||||||
[Key("viewer_id")]
|
[Key("viewer_id")]
|
||||||
public string ViewerId { get; set; }
|
public string ViewerId { get; set; }
|
||||||
[Key("steam_id")]
|
[Key("steam_id")]
|
||||||
public long SteamId { get; set; }
|
public ulong SteamId { get; set; }
|
||||||
[Key("steam_session_ticket")]
|
[Key("steam_session_ticket")]
|
||||||
public string SteamSessionTicket { get; set; }
|
public string SteamSessionTicket { get; set; }
|
||||||
}
|
}
|
||||||
@@ -7,8 +7,10 @@ public class TransitionAccountData
|
|||||||
{
|
{
|
||||||
[Key("social_account_id")]
|
[Key("social_account_id")]
|
||||||
public string SocialAccountId { get; set; }
|
public string SocialAccountId { get; set; }
|
||||||
|
|
||||||
[Key("social_account_type")]
|
[Key("social_account_type")]
|
||||||
public string SocialAccountType { get; set; }
|
public string SocialAccountType { get; set; }
|
||||||
|
|
||||||
[Key("connected_viewer_id")]
|
[Key("connected_viewer_id")]
|
||||||
public string ConnectedViewerId { get; set; }
|
public string ConnectedViewerId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,10 @@ using DCGEngine.Database.Configuration;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using SVSim.Database;
|
using SVSim.Database;
|
||||||
using SVSim.Database.Models;
|
using SVSim.Database.Models;
|
||||||
|
using SVSim.Database.Repositories.Viewer;
|
||||||
using SVSim.EmulatedEntrypoint.Middlewares;
|
using SVSim.EmulatedEntrypoint.Middlewares;
|
||||||
|
using SVSim.EmulatedEntrypoint.Security.SteamSessionAuthentication;
|
||||||
|
using SVSim.EmulatedEntrypoint.Services;
|
||||||
|
|
||||||
namespace SVSim.EmulatedEntrypoint;
|
namespace SVSim.EmulatedEntrypoint;
|
||||||
|
|
||||||
@@ -24,18 +27,42 @@ public class Program
|
|||||||
{
|
{
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#region Database Services
|
||||||
|
|
||||||
builder.Services.AddDbContext<SVSimDbContext>(opt =>
|
builder.Services.AddDbContext<SVSimDbContext>(opt =>
|
||||||
{
|
{
|
||||||
opt.UseSqlite();
|
opt.UseSqlite(builder.Configuration.GetConnectionString("Sqlite"));
|
||||||
});
|
});
|
||||||
|
builder.Services.AddTransient<IViewerRepository, ViewerRepository>();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
builder.Services.AddTransient<ShadowverseTranslationMiddleware>();
|
builder.Services.AddTransient<ShadowverseTranslationMiddleware>();
|
||||||
|
builder.Services.AddTransient<SessionidMappingMiddleware>();
|
||||||
builder.Services.Configure<DCGEDatabaseConfiguration>(opt =>
|
builder.Services.Configure<DCGEDatabaseConfiguration>(opt =>
|
||||||
{
|
{
|
||||||
opt.DbSetSearchAssemblies = new List<Assembly> { Assembly.GetAssembly(typeof(SVSimDbContext)) };
|
opt.DbSetSearchAssemblies = new List<Assembly> { Assembly.GetAssembly(typeof(SVSimDbContext)) };
|
||||||
});
|
});
|
||||||
|
builder.Services.AddSingleton<ShadowverseSessionService>();
|
||||||
|
builder.Services.AddSingleton<SteamSessionService>();
|
||||||
|
builder.Services.AddAuthentication()
|
||||||
|
.AddScheme<SteamAuthenticationHandlerOptions, SteamSessionAuthenticationHandler>(
|
||||||
|
SteamAuthenticationConstants.SchemeName,
|
||||||
|
opt =>
|
||||||
|
{
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Update database
|
||||||
|
using (var scope = app.Services.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||||
|
dbContext.UpdateDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
app.UseHttpLogging();
|
app.UseHttpLogging();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
@@ -47,8 +74,12 @@ public class Program
|
|||||||
|
|
||||||
//app.UseHttpsRedirection();
|
//app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.UseMiddleware<SessionidMappingMiddleware>();
|
||||||
|
|
||||||
app.UseMiddleware<ShadowverseTranslationMiddleware>();
|
app.UseMiddleware<ShadowverseTranslationMiddleware>();
|
||||||
|
|
||||||
|
app.UseAuthentication();
|
||||||
|
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,4 +32,29 @@
|
|||||||
<ProjectReference Include="..\SVSim.Database\SVSim.Database.csproj" />
|
<ProjectReference Include="..\SVSim.Database\SVSim.Database.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="lib\libsteam_api.so">
|
||||||
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="lib\libsteam_api.so.meta">
|
||||||
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="lib\steam_api.dll">
|
||||||
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="lib\steam_api.lib">
|
||||||
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="lib\steam_api64.dll">
|
||||||
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="lib\steam_api64.lib">
|
||||||
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
|
<Exec Command="COPY "$(ProjectDir)\lib\*" "$(TargetDir)"" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -58,27 +58,29 @@ public static class Encryption
|
|||||||
/// <returns>the decrypted bytes</returns>
|
/// <returns>the decrypted bytes</returns>
|
||||||
public static byte[] Decrypt(byte[] encryptedData, string udId)
|
public static byte[] Decrypt(byte[] encryptedData, string udId)
|
||||||
{
|
{
|
||||||
using (var rj = Aes.Create())
|
using (var rj = new RijndaelManaged())
|
||||||
{
|
{
|
||||||
rj.KeySize = EncryptionKeySize;
|
rj.KeySize = EncryptionKeySize;
|
||||||
rj.Mode = EncryptionMode;
|
rj.Mode = EncryptionMode;
|
||||||
rj.BlockSize = EncryptionBlockSize;
|
rj.BlockSize = EncryptionBlockSize;
|
||||||
|
//rj.Padding = PaddingMode.None;
|
||||||
byte[] rgbIv = Encoding.UTF8.GetBytes(udId.Replace("-", string.Empty).Substring(0, UdIdKeySize));
|
byte[] rgbIv = Encoding.UTF8.GetBytes(udId.Replace("-", string.Empty).Substring(0, UdIdKeySize));
|
||||||
byte[] keyBytes = new byte[KeyStringSize];
|
byte[] keyBytes = new byte[KeyStringSize];
|
||||||
byte[] encryptedValueBytes = new byte[encryptedData.Length - KeyStringSize];
|
byte[] encryptedValueBytes = new byte[encryptedData.Length - KeyStringSize];
|
||||||
Array.Copy(encryptedData, encryptedData.Length - keyBytes.Length, keyBytes, 0, keyBytes.Length);
|
Array.Copy(encryptedData, encryptedData.Length - keyBytes.Length, keyBytes, 0, keyBytes.Length);
|
||||||
Array.Copy(encryptedData, 0, encryptedValueBytes, 0, encryptedValueBytes.Length);
|
Array.Copy(encryptedData, 0, encryptedValueBytes, 0, encryptedValueBytes.Length);
|
||||||
ICryptoTransform transform = rj.CreateDecryptor(keyBytes, rgbIv);
|
ICryptoTransform transform = rj.CreateDecryptor(keyBytes, rgbIv);
|
||||||
|
byte[] decryptedValueBytes = new byte[encryptedValueBytes.Length];
|
||||||
using (MemoryStream ms = new MemoryStream(encryptedValueBytes))
|
using (MemoryStream ms = new MemoryStream(encryptedValueBytes))
|
||||||
{
|
{
|
||||||
using (CryptoStream cs = new CryptoStream(ms, transform, CryptoStreamMode.Read))
|
using (CryptoStream cs = new CryptoStream(ms, transform, CryptoStreamMode.Read))
|
||||||
{
|
{
|
||||||
byte[] decryptedValueBytes = new byte[encryptedValueBytes.Length];
|
cs.CopyTo(decryptedValueBytes);
|
||||||
cs.Read(decryptedValueBytes, 0, encryptedValueBytes.Length);
|
cs.Flush();
|
||||||
cs.FlushFinalBlock();
|
ms.Flush();
|
||||||
return decryptedValueBytes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return decryptedValueBytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace SVSim.EmulatedEntrypoint.Security.SteamSessionAuthentication;
|
||||||
|
|
||||||
|
public static class SteamAuthenticationConstants
|
||||||
|
{
|
||||||
|
public const string SchemeName = "SteamAuthentication";
|
||||||
|
public const string SteamIdClaim = "SteamId";
|
||||||
|
}
|
||||||
@@ -1,21 +1,74 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using SVSim.Database.Enums;
|
||||||
|
using SVSim.Database.Models;
|
||||||
|
using SVSim.Database.Repositories.Viewer;
|
||||||
|
using SVSim.EmulatedEntrypoint.Constants;
|
||||||
|
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests;
|
||||||
|
using SVSim.EmulatedEntrypoint.Services;
|
||||||
|
|
||||||
namespace SVSim.EmulatedEntrypoint.Security.SteamSessionAuthentication;
|
namespace SVSim.EmulatedEntrypoint.Security.SteamSessionAuthentication;
|
||||||
|
|
||||||
public class SteamSessionAuthenticationHandler : AuthenticationHandler<SteamAuthenticationHandlerOptions>
|
public class SteamSessionAuthenticationHandler : AuthenticationHandler<SteamAuthenticationHandlerOptions>
|
||||||
{
|
{
|
||||||
public SteamSessionAuthenticationHandler(IOptionsMonitor<SteamAuthenticationHandlerOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
|
private readonly SteamSessionService _sessionService;
|
||||||
{
|
private readonly IViewerRepository _viewerRepository;
|
||||||
}
|
public SteamSessionAuthenticationHandler(IOptionsMonitor<SteamAuthenticationHandlerOptions> options, ILoggerFactory logger, UrlEncoder encoder, SteamSessionService sessionService, IViewerRepository viewerRepository) : base(options, logger, encoder)
|
||||||
|
|
||||||
public SteamSessionAuthenticationHandler(IOptionsMonitor<SteamAuthenticationHandlerOptions> options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder)
|
|
||||||
{
|
{
|
||||||
|
_sessionService = sessionService;
|
||||||
|
_viewerRepository = viewerRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async override Task<AuthenticateResult> HandleAuthenticateAsync()
|
protected async override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||||
{
|
{
|
||||||
return AuthenticateResult.Fail("Not implemented");
|
byte[] requestBytes;
|
||||||
|
using (var requestBytesStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
await Request.Body.CopyToAsync(requestBytesStream);
|
||||||
|
requestBytes = requestBytesStream.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert bytes to json
|
||||||
|
string requestString = Encoding.UTF8.GetString(requestBytes);
|
||||||
|
BaseRequest? requestJson = JsonConvert.DeserializeObject<BaseRequest>(requestString);
|
||||||
|
|
||||||
|
// Reset request stream
|
||||||
|
Request.Body.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
if (requestJson is null)
|
||||||
|
{
|
||||||
|
return AuthenticateResult.Fail("Invalid request body.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check steam session validity
|
||||||
|
bool sessionIsValid = _sessionService.IsTicketValidForUser(requestJson.SteamSessionTicket, requestJson.SteamId);
|
||||||
|
if (!sessionIsValid)
|
||||||
|
{
|
||||||
|
return AuthenticateResult.Fail("Invalid ticket.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Viewer? viewer =
|
||||||
|
await _viewerRepository.GetViewerBySocialConnection(SocialAccountType.Steam, requestJson.SteamId);
|
||||||
|
|
||||||
|
if (viewer is null)
|
||||||
|
{
|
||||||
|
return AuthenticateResult.Fail("User not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build identity
|
||||||
|
ClaimsIdentity identity = new ClaimsIdentity();
|
||||||
|
identity.AddClaim(new Claim(ClaimTypes.Name, viewer.DisplayName));
|
||||||
|
identity.AddClaim(new Claim(ShadowverseClaimTypes.ShortUdidClaim, viewer.ShortUdid.ToString()));
|
||||||
|
identity.AddClaim(new Claim(ShadowverseClaimTypes.ViewerIdClaim, viewer.Id.ToString()));
|
||||||
|
identity.AddClaim(new Claim(SteamAuthenticationConstants.SteamIdClaim, requestJson.SteamId.ToString()));
|
||||||
|
|
||||||
|
// Build and return final ticket
|
||||||
|
AuthenticationTicket ticket =
|
||||||
|
new AuthenticationTicket(new ClaimsPrincipal(), SteamAuthenticationConstants.SchemeName);
|
||||||
|
return AuthenticateResult.Success(ticket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace SVSim.EmulatedEntrypoint.Services;
|
||||||
|
|
||||||
|
public class ShadowverseSessionService
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<string, Guid> _sessionIdToUdid;
|
||||||
|
|
||||||
|
public ShadowverseSessionService()
|
||||||
|
{
|
||||||
|
_sessionIdToUdid = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid? GetUdidFromSessionId(string sid)
|
||||||
|
{
|
||||||
|
if (_sessionIdToUdid.TryGetValue(sid, out var udid))
|
||||||
|
{
|
||||||
|
return udid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StoreUdidForSessionId(string sid, Guid udid)
|
||||||
|
{
|
||||||
|
_sessionIdToUdid.AddOrUpdate(sid, _ => udid, (_, _) => udid);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace SVSim.EmulatedEntrypoint;
|
|
||||||
|
|
||||||
public class WeatherForecast
|
|
||||||
{
|
|
||||||
public DateOnly Date { get; set; }
|
|
||||||
|
|
||||||
public int TemperatureC { get; set; }
|
|
||||||
|
|
||||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
|
||||||
|
|
||||||
public string? Summary { get; set; }
|
|
||||||
}
|
|
||||||
@@ -6,5 +6,8 @@
|
|||||||
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
|
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"Sqlite": "Data Source=test_db"
|
||||||
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*"
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
SVSim.EmulatedEntrypoint/lib/libsteam_api.so
Normal file
BIN
SVSim.EmulatedEntrypoint/lib/libsteam_api.so
Normal file
Binary file not shown.
Binary file not shown.
BIN
SVSim.EmulatedEntrypoint/lib/steam_api.lib
Normal file
BIN
SVSim.EmulatedEntrypoint/lib/steam_api.lib
Normal file
Binary file not shown.
Binary file not shown.
BIN
SVSim.EmulatedEntrypoint/lib/steam_api64.lib
Normal file
BIN
SVSim.EmulatedEntrypoint/lib/steam_api64.lib
Normal file
Binary file not shown.
@@ -16,6 +16,6 @@ public class Tests
|
|||||||
"140000005ee7d30c1263e214e133a10001001001e07cd866180000000100000002000000b8526bb7b8946cd27c214574f1000000b20000003200000004000000e133a1000100100168eb0600488cc2443101a8c0000000008165d4660115f06601005c7e010000000000cad61456a2b83d39595c3e3749b96b4537ebde88d048103a6f6c7b2b81ee68711378836872a11422f5bd16fad803f81122c5ae98d986b693bbbc00ac7d30a8f85af2c1a7dce57751eb2c7f21130284aa8d9ee787246c8ccc138f05936bacb1ba4baba5fa5fbf6158002cf7207ae25a6f6ee8e3fc8edbb84903d346a249179637";
|
"140000005ee7d30c1263e214e133a10001001001e07cd866180000000100000002000000b8526bb7b8946cd27c214574f1000000b20000003200000004000000e133a1000100100168eb0600488cc2443101a8c0000000008165d4660115f06601005c7e010000000000cad61456a2b83d39595c3e3749b96b4537ebde88d048103a6f6c7b2b81ee68711378836872a11422f5bd16fad803f81122c5ae98d986b693bbbc00ac7d30a8f85af2c1a7dce57751eb2c7f21130284aa8d9ee787246c8ccc138f05936bacb1ba4baba5fa5fbf6158002cf7207ae25a6f6ee8e3fc8edbb84903d346a249179637";
|
||||||
using var steamService = new SteamSessionService();
|
using var steamService = new SteamSessionService();
|
||||||
bool validTicket = steamService.IsTicketValidForUser(ticket, 76561197970830305);
|
bool validTicket = steamService.IsTicketValidForUser(ticket, 76561197970830305);
|
||||||
Assert.AreEqual(true, validTicket);
|
Assert.That(validTicket, Is.EqualTo(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user