From 2310e7d2b5af16de59abaa5fa273b732ad6abb38 Mon Sep 17 00:00:00 2001 From: Morbo <14136326+Morb0@users.noreply.github.com> Date: Wed, 28 Sep 2022 06:21:09 +0300 Subject: [PATCH 1/9] Add Discord linking --- .../Config/DiscordConfiguration.cs | 8 + SS14.Auth.Shared/Data/ApplicationDbContext.cs | 9 + SS14.Auth.Shared/Data/Discord.cs | 14 + .../20220928002800_Discord.Designer.cs | 1612 +++++++++++++++++ .../Data/Migrations/20220928002800_Discord.cs | 52 + .../ApplicationDbContextModelSnapshot.cs | 39 + SS14.Auth.Shared/Data/SpaceUser.cs | 1 + SS14.Auth.Shared/StartupHelpers.cs | 1 + .../Pages/Account/Manage/ManageDiscord.cshtml | 33 + .../Account/Manage/ManageDiscord.cshtml.cs | 87 + .../Pages/Account/Manage/ManageNavPages.cs | 2 + .../Pages/Account/Manage/_ManageNav.cshtml | 1 + SS14.Web/DiscordConnectionHandler.cs | 65 + SS14.Web/SS14.Web.csproj | 3 +- SS14.Web/Startup.cs | 16 + 15 files changed, 1942 insertions(+), 1 deletion(-) create mode 100644 SS14.Auth.Shared/Config/DiscordConfiguration.cs create mode 100644 SS14.Auth.Shared/Data/Discord.cs create mode 100644 SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.Designer.cs create mode 100644 SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.cs create mode 100644 SS14.Web/Areas/Identity/Pages/Account/Manage/ManageDiscord.cshtml create mode 100644 SS14.Web/Areas/Identity/Pages/Account/Manage/ManageDiscord.cshtml.cs create mode 100644 SS14.Web/DiscordConnectionHandler.cs diff --git a/SS14.Auth.Shared/Config/DiscordConfiguration.cs b/SS14.Auth.Shared/Config/DiscordConfiguration.cs new file mode 100644 index 0000000..948f81b --- /dev/null +++ b/SS14.Auth.Shared/Config/DiscordConfiguration.cs @@ -0,0 +1,8 @@ +namespace SS14.Auth.Shared.Config +{ + public sealed class DiscordConfiguration + { + public string ClientId { get; set; } + public string ClientSecret { get; set; } + } +} \ No newline at end of file diff --git a/SS14.Auth.Shared/Data/ApplicationDbContext.cs b/SS14.Auth.Shared/Data/ApplicationDbContext.cs index ec98d87..26f5bd4 100644 --- a/SS14.Auth.Shared/Data/ApplicationDbContext.cs +++ b/SS14.Auth.Shared/Data/ApplicationDbContext.cs @@ -69,6 +69,14 @@ protected override void OnModelCreating(ModelBuilder builder) .HasIndex(p => p.SpaceUserId) .IsUnique(); + builder.Entity() + .HasIndex(p => p.DiscordId) + .IsUnique(); + + builder.Entity() + .HasIndex(p => p.SpaceUserId) + .IsUnique(); + builder.Entity() .HasIndex(p => new { p.ClientId }) .IsUnique(); @@ -111,6 +119,7 @@ protected override void OnModelCreating(ModelBuilder builder) public DbSet DataProtectionKeys { get; set; } public DbSet BurnerEmails { get; set; } public DbSet Patrons { get; set; } + public DbSet Discords { get; set; } public DbSet PatreonWebhookLogs { get; set; } public DbSet UserOAuthClients { get; set; } diff --git a/SS14.Auth.Shared/Data/Discord.cs b/SS14.Auth.Shared/Data/Discord.cs new file mode 100644 index 0000000..eb74543 --- /dev/null +++ b/SS14.Auth.Shared/Data/Discord.cs @@ -0,0 +1,14 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace SS14.Auth.Shared.Data +{ + public class Discord + { + public int Id { get; set; } + + [Required] public string DiscordId { get; set; } + [Required] public Guid SpaceUserId { get; set; } + public SpaceUser SpaceUser { get; set; } + } +} \ No newline at end of file diff --git a/SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.Designer.cs b/SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.Designer.cs new file mode 100644 index 0000000..400797f --- /dev/null +++ b/SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.Designer.cs @@ -0,0 +1,1612 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using SS14.Auth.Shared.Data; + +#nullable disable + +namespace SS14.Auth.Shared.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20220928002800_Discord")] + partial class Discord + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AllowedAccessTokenSigningAlgorithms") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("LastAccessed") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("NonEditable") + .HasColumnType("boolean"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.Property("Updated") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ApiResources", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApiResourceId") + .HasColumnType("integer"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("ApiResourceId"); + + b.ToTable("ApiResourceClaims", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApiResourceId") + .HasColumnType("integer"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("Id"); + + b.HasIndex("ApiResourceId"); + + b.ToTable("ApiResourceProperties", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApiResourceId") + .HasColumnType("integer"); + + b.Property("Scope") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("ApiResourceId"); + + b.ToTable("ApiResourceScopes", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApiResourceId") + .HasColumnType("integer"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.HasKey("Id"); + + b.HasIndex("ApiResourceId"); + + b.ToTable("ApiResourceSecrets", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Emphasize") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ApiScopes", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ScopeId") + .HasColumnType("integer"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("ScopeId"); + + b.ToTable("ApiScopeClaims", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("ScopeId") + .HasColumnType("integer"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("Id"); + + b.HasIndex("ScopeId"); + + b.ToTable("ApiScopeProperties", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AbsoluteRefreshTokenLifetime") + .HasColumnType("integer"); + + b.Property("AccessTokenLifetime") + .HasColumnType("integer"); + + b.Property("AccessTokenType") + .HasColumnType("integer"); + + b.Property("AllowAccessTokensViaBrowser") + .HasColumnType("boolean"); + + b.Property("AllowOfflineAccess") + .HasColumnType("boolean"); + + b.Property("AllowPlainTextPkce") + .HasColumnType("boolean"); + + b.Property("AllowRememberConsent") + .HasColumnType("boolean"); + + b.Property("AllowedIdentityTokenSigningAlgorithms") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("AlwaysIncludeUserClaimsInIdToken") + .HasColumnType("boolean"); + + b.Property("AlwaysSendClientClaims") + .HasColumnType("boolean"); + + b.Property("AuthorizationCodeLifetime") + .HasColumnType("integer"); + + b.Property("BackChannelLogoutSessionRequired") + .HasColumnType("boolean"); + + b.Property("BackChannelLogoutUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ClientClaimsPrefix") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ConsentLifetime") + .HasColumnType("integer"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DeviceCodeLifetime") + .HasColumnType("integer"); + + b.Property("EnableLocalLogin") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("FrontChannelLogoutSessionRequired") + .HasColumnType("boolean"); + + b.Property("FrontChannelLogoutUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("IdentityTokenLifetime") + .HasColumnType("integer"); + + b.Property("IncludeJwtId") + .HasColumnType("boolean"); + + b.Property("LastAccessed") + .HasColumnType("timestamp with time zone"); + + b.Property("LogoUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("NonEditable") + .HasColumnType("boolean"); + + b.Property("PairWiseSubjectSalt") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ProtocolType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RefreshTokenExpiration") + .HasColumnType("integer"); + + b.Property("RefreshTokenUsage") + .HasColumnType("integer"); + + b.Property("RequireClientSecret") + .HasColumnType("boolean"); + + b.Property("RequireConsent") + .HasColumnType("boolean"); + + b.Property("RequirePkce") + .HasColumnType("boolean"); + + b.Property("RequireRequestObject") + .HasColumnType("boolean"); + + b.Property("SlidingRefreshTokenLifetime") + .HasColumnType("integer"); + + b.Property("UpdateAccessTokenClaimsOnRefresh") + .HasColumnType("boolean"); + + b.Property("Updated") + .HasColumnType("timestamp with time zone"); + + b.Property("UserCodeType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UserSsoLifetime") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("Clients", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientClaims", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Origin") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientCorsOrigins", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("GrantType") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientGrantTypes", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientIdPRestrictions", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("PostLogoutRedirectUri") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientPostLogoutRedirectUris", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientProperties", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("RedirectUri") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientRedirectUris", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Scope") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientScopes", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientSecrets", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => + { + b.Property("UserCode") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("character varying(50000)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Expiration") + .IsRequired() + .HasColumnType("timestamp with time zone"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("UserCode"); + + b.HasIndex("DeviceCode") + .IsUnique(); + + b.HasIndex("Expiration"); + + b.ToTable("DeviceCodes", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Emphasize") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("NonEditable") + .HasColumnType("boolean"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.Property("Updated") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("IdentityResources", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IdentityResourceId") + .HasColumnType("integer"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityResourceId"); + + b.ToTable("IdentityResourceClaims", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IdentityResourceId") + .HasColumnType("integer"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityResourceId"); + + b.ToTable("IdentityResourceProperties", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConsumedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("character varying(50000)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.HasIndex("Expiration"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.HasIndex("SubjectId", "SessionId", "Type"); + + b.ToTable("PersistedGrants", "IS4"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.AuthHash", b => + { + b.Property("AuthHashId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("AuthHashId")); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.HasKey("AuthHashId"); + + b.HasIndex("SpaceUserId"); + + b.HasIndex("Hash", "SpaceUserId") + .IsUnique(); + + b.ToTable("AuthHashes"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.BurnerEmail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Domain") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Domain") + .IsUnique(); + + b.ToTable("BurnerEmails"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.Discord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DiscordId") + .IsRequired() + .HasColumnType("text"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DiscordId") + .IsUnique(); + + b.HasIndex("SpaceUserId") + .IsUnique(); + + b.ToTable("Discords"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.LoginSession", b => + { + b.Property("LoginSessionId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("LoginSessionId")); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.Property("Token") + .IsRequired() + .HasColumnType("bytea"); + + b.HasKey("LoginSessionId"); + + b.HasIndex("SpaceUserId"); + + b.HasIndex("Token") + .IsUnique(); + + b.ToTable("ActiveSessions"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.PatreonWebhookLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Content") + .HasColumnType("jsonb"); + + b.Property("Time") + .HasColumnType("timestamp with time zone"); + + b.Property("Trigger") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("PatreonWebhookLogs"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.Patron", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CurrentTier") + .HasColumnType("text"); + + b.Property("PatreonId") + .IsRequired() + .HasColumnType("text"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PatreonId") + .IsUnique(); + + b.HasIndex("SpaceUserId") + .IsUnique(); + + b.ToTable("Patrons"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.SpaceRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.SpaceUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.UserOAuthClient", b => + { + b.Property("UserOAuthClientId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("UserOAuthClientId")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.HasKey("UserOAuthClientId"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.HasIndex("SpaceUserId"); + + b.ToTable("UserOAuthClients"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiResource"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") + .WithMany("Properties") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiResource"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") + .WithMany("Scopes") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiResource"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") + .WithMany("Secrets") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiResource"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "Scope") + .WithMany("UserClaims") + .HasForeignKey("ScopeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Scope"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "Scope") + .WithMany("Properties") + .HasForeignKey("ScopeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Scope"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("Claims") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("AllowedCorsOrigins") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("AllowedGrantTypes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("IdentityProviderRestrictions") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("PostLogoutRedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("Properties") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("RedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("AllowedScopes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("ClientSecrets") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") + .WithMany("UserClaims") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityResource"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") + .WithMany("Properties") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityResource"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.AuthHash", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithMany("AuthHashes") + .HasForeignKey("SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpaceUser"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.Discord", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithOne("Discord") + .HasForeignKey("SS14.Auth.Shared.Data.Discord", "SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpaceUser"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.LoginSession", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithMany("LoginSessions") + .HasForeignKey("SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpaceUser"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.Patron", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithOne("Patron") + .HasForeignKey("SS14.Auth.Shared.Data.Patron", "SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpaceUser"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.UserOAuthClient", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithMany() + .HasForeignKey("SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + + b.Navigation("SpaceUser"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b => + { + b.Navigation("Properties"); + + b.Navigation("Scopes"); + + b.Navigation("Secrets"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => + { + b.Navigation("Properties"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b => + { + b.Navigation("AllowedCorsOrigins"); + + b.Navigation("AllowedGrantTypes"); + + b.Navigation("AllowedScopes"); + + b.Navigation("Claims"); + + b.Navigation("ClientSecrets"); + + b.Navigation("IdentityProviderRestrictions"); + + b.Navigation("PostLogoutRedirectUris"); + + b.Navigation("Properties"); + + b.Navigation("RedirectUris"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b => + { + b.Navigation("Properties"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.SpaceUser", b => + { + b.Navigation("AuthHashes"); + + b.Navigation("Discord"); + + b.Navigation("LoginSessions"); + + b.Navigation("Patron"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.cs b/SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.cs new file mode 100644 index 0000000..45e5642 --- /dev/null +++ b/SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.cs @@ -0,0 +1,52 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace SS14.Auth.Shared.Data.Migrations +{ + public partial class Discord : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Discords", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + DiscordId = table.Column(type: "text", nullable: false), + SpaceUserId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Discords", x => x.Id); + table.ForeignKey( + name: "FK_Discords_AspNetUsers_SpaceUserId", + column: x => x.SpaceUserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Discords_DiscordId", + table: "Discords", + column: "DiscordId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Discords_SpaceUserId", + table: "Discords", + column: "SpaceUserId", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Discords"); + } + } +} diff --git a/SS14.Auth.Shared/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/SS14.Auth.Shared/Data/Migrations/ApplicationDbContextModelSnapshot.cs index d43bf53..9f6b551 100644 --- a/SS14.Auth.Shared/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/SS14.Auth.Shared/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1041,6 +1041,32 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("BurnerEmails"); }); + modelBuilder.Entity("SS14.Auth.Shared.Data.Discord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DiscordId") + .IsRequired() + .HasColumnType("text"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DiscordId") + .IsUnique(); + + b.HasIndex("SpaceUserId") + .IsUnique(); + + b.ToTable("Discords"); + }); + modelBuilder.Entity("SS14.Auth.Shared.Data.LoginSession", b => { b.Property("LoginSessionId") @@ -1470,6 +1496,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("SpaceUser"); }); + modelBuilder.Entity("SS14.Auth.Shared.Data.Discord", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithOne("Discord") + .HasForeignKey("SS14.Auth.Shared.Data.Discord", "SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpaceUser"); + }); + modelBuilder.Entity("SS14.Auth.Shared.Data.LoginSession", b => { b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") @@ -1561,6 +1598,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.Navigation("AuthHashes"); + b.Navigation("Discord"); + b.Navigation("LoginSessions"); b.Navigation("Patron"); diff --git a/SS14.Auth.Shared/Data/SpaceUser.cs b/SS14.Auth.Shared/Data/SpaceUser.cs index facc782..d9e8729 100644 --- a/SS14.Auth.Shared/Data/SpaceUser.cs +++ b/SS14.Auth.Shared/Data/SpaceUser.cs @@ -11,5 +11,6 @@ public class SpaceUser : IdentityUser public List AuthHashes { get; set; } = new List(); public Patron Patron { get; set; } + public Discord Discord { get; set; } } } \ No newline at end of file diff --git a/SS14.Auth.Shared/StartupHelpers.cs b/SS14.Auth.Shared/StartupHelpers.cs index efe6dc4..bf5628d 100644 --- a/SS14.Auth.Shared/StartupHelpers.cs +++ b/SS14.Auth.Shared/StartupHelpers.cs @@ -27,6 +27,7 @@ public static void AddShared(IServiceCollection services, IConfiguration config) services.Configure(config.GetSection("Limits")); services.Configure(config.GetSection("Mutex")); services.Configure(config.GetSection("Patreon")); + services.Configure(config.GetSection("Discord")); services.Configure(options => { // The fact that this isn't default absolutely baffles me. diff --git a/SS14.Web/Areas/Identity/Pages/Account/Manage/ManageDiscord.cshtml b/SS14.Web/Areas/Identity/Pages/Account/Manage/ManageDiscord.cshtml new file mode 100644 index 0000000..f8e3741 --- /dev/null +++ b/SS14.Web/Areas/Identity/Pages/Account/Manage/ManageDiscord.cshtml @@ -0,0 +1,33 @@ +@page +@model ManageDiscord + +@{ + ViewData["Title"] = "Discord"; + ViewData["ActivePage"] = ManageNavPages.ManageDiscord; +} + +

@ViewData["Title"]

+ +
+
+ @if (Model.DiscordLinked) + { +

+ Your Discord account is linked to your Space Station 14 account.
+

+ + + } + else + { +

+ You can link your Discord account with your Space Station 14 account here to allow servers verify you. +

+ + } +
+
\ No newline at end of file diff --git a/SS14.Web/Areas/Identity/Pages/Account/Manage/ManageDiscord.cshtml.cs b/SS14.Web/Areas/Identity/Pages/Account/Manage/ManageDiscord.cshtml.cs new file mode 100644 index 0000000..24f12cf --- /dev/null +++ b/SS14.Web/Areas/Identity/Pages/Account/Manage/ManageDiscord.cshtml.cs @@ -0,0 +1,87 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using SS14.Auth.Shared.Config; +using SS14.Auth.Shared.Data; + +namespace SS14.Web.Areas.Identity.Pages.Account.Manage +{ + public class ManageDiscord : PageModel + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + private readonly ApplicationDbContext _db; + private readonly IOptions _cfg; + + public bool DiscordLinked { get; private set; } + + public ManageDiscord( + UserManager userManager, + ILogger logger, + ApplicationDbContext db) + { + _userManager = userManager; + _logger = logger; + _db = db; + } + + public async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var discord = await _db.Discords.SingleOrDefaultAsync(p => p.SpaceUserId == user.Id); + + DiscordLinked = discord != null; + + return Page(); + } + + public async Task OnPostUnlinkDiscordAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var discord = await _db.Discords.SingleOrDefaultAsync(p => p.SpaceUserId == user.Id); + + if (discord != null) + { + _db.Discords.Remove(discord); + await _db.SaveChangesAsync(); + } + + return RedirectToPage(); + } + + public async Task OnPostLinkDiscordAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var redirect = Url.Page("./ManageDiscord"); + + return Challenge(new AuthenticationProperties + { + Items = + { + ["SS14UserId"] = user.Id.ToString(), + }, + RedirectUri = redirect + }, "Discord"); + } + } +} \ No newline at end of file diff --git a/SS14.Web/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs b/SS14.Web/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs index d77ed3c..12b89d1 100644 --- a/SS14.Web/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs +++ b/SS14.Web/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs @@ -16,6 +16,7 @@ public static class ManageNavPages public const string ExternalLogins = "ExternalLogins"; public const string PersonalData = "PersonalData"; public const string TwoFactorAuthentication = "TwoFactorAuthentication"; + public const string ManageDiscord = "ManageDiscord"; public const string ManagePatreon = "ManagePatreon"; public const string Developer = "Developer"; @@ -43,6 +44,7 @@ public static string PersonalDataNavClass(ViewContext viewContext) public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication); + public static string DiscordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ManageDiscord); public static string PatreonNavClass(ViewContext viewContext) => PageNavClass(viewContext, ManagePatreon); public static string DeveloperNavClass(ViewContext viewContext) => PageNavClass(viewContext, Developer); diff --git a/SS14.Web/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml b/SS14.Web/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml index 83a5af0..45c87d2 100644 --- a/SS14.Web/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml +++ b/SS14.Web/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml @@ -16,6 +16,7 @@ *@ + diff --git a/SS14.Web/DiscordConnectionHandler.cs b/SS14.Web/DiscordConnectionHandler.cs new file mode 100644 index 0000000..365130e --- /dev/null +++ b/SS14.Web/DiscordConnectionHandler.cs @@ -0,0 +1,65 @@ +using System; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.OAuth; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using SS14.Auth.Shared.Data; + +namespace SS14.Web +{ + public sealed class DiscordConnectionHandler + { + private readonly UserManager _userManager; + private readonly ApplicationDbContext _db; + + public DiscordConnectionHandler( + UserManager userManager, + ApplicationDbContext db) + { + _userManager = userManager; + _db = db; + } + public async Task HookReceivedTicket(TicketReceivedContext context) + { + var guid = context.Properties.Items["SS14UserId"]!; + var user = await _userManager.FindByIdAsync(guid); + if (user == null) + { + throw new InvalidOperationException("Unable to find user on hook"); + } + + var discordId = context.Principal!.Claims.First(p => p.Type == ClaimTypes.NameIdentifier).Value; + var existingDiscord = await _db.Discords.FirstOrDefaultAsync(a => a.DiscordId == discordId); + if (existingDiscord != null) + { + // Relinking + _db.Discords.Remove(existingDiscord); + } + + existingDiscord = await _db.Discords.FirstOrDefaultAsync(a => a.SpaceUserId == user.Id); + if (existingDiscord != null) + { + // Relinking + _db.Discords.Remove(existingDiscord); + } + + var discordLink = new Discord + { + DiscordId = discordId, + SpaceUserId = user.Id + }; + _db.Discords.Add(discordLink); + await _db.SaveChangesAsync(); + + if (context.ReturnUri != null) + { + context.HttpContext.Response.Redirect(context.ReturnUri); + } + + context.HandleResponse(); + } + } +} \ No newline at end of file diff --git a/SS14.Web/SS14.Web.csproj b/SS14.Web/SS14.Web.csproj index 70974bf..88b7bdf 100644 --- a/SS14.Web/SS14.Web.csproj +++ b/SS14.Web/SS14.Web.csproj @@ -9,10 +9,11 @@ + - + diff --git a/SS14.Web/Startup.cs b/SS14.Web/Startup.cs index b9bdf4e..11e0e2a 100644 --- a/SS14.Web/Startup.cs +++ b/SS14.Web/Startup.cs @@ -61,6 +61,7 @@ public void ConfigureServices(IServiceCollection services) services.AddRazorPages(); services.AddScoped(); + services.AddScoped(); var patreonSection = Configuration.GetSection("Patreon"); var patreonCfg = patreonSection.Get(); @@ -94,6 +95,21 @@ public void ConfigureServices(IServiceCollection services) }); } + var discordCfg = Configuration.GetSection("Discord").Get(); + if (discordCfg?.ClientId != null && discordCfg?.ClientSecret != null) + { + services.AddAuthentication() + .AddDiscord("Discord", null!, options => + { + options.ClientId = discordCfg.ClientId; + options.ClientSecret = discordCfg.ClientSecret; + options.Events.OnTicketReceived += context => + { + var handler = context.HttpContext.RequestServices.GetService(); + return handler!.HookReceivedTicket(context); + }; + }); + } var builder = services.AddIdentityServer(options => { From c9cefaa51d87c306f79822d3ed40afe7cd690174 Mon Sep 17 00:00:00 2001 From: Morbo <14136326+Morb0@users.noreply.github.com> Date: Wed, 28 Sep 2022 06:44:56 +0300 Subject: [PATCH 2/9] Add discord id to user API --- SS14.Auth.Shared/Data/DiscordDataManager.cs | 20 +++++++++++++++++++ SS14.Auth.Shared/StartupHelpers.cs | 1 + SS14.Auth/Controllers/QueryApiController.cs | 10 +++++++--- SS14.Auth/Controllers/SessionApiController.cs | 7 +++++-- SS14.Auth/Responses/QueryUserResponse.cs | 1 + 5 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 SS14.Auth.Shared/Data/DiscordDataManager.cs diff --git a/SS14.Auth.Shared/Data/DiscordDataManager.cs b/SS14.Auth.Shared/Data/DiscordDataManager.cs new file mode 100644 index 0000000..00389f8 --- /dev/null +++ b/SS14.Auth.Shared/Data/DiscordDataManager.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; + +namespace SS14.Auth.Shared.Data +{ + public sealed class DiscordDataManager + { + private readonly ApplicationDbContext _db; + + public DiscordDataManager(ApplicationDbContext db) + { + _db = db; + } + + public async Task GetUserDiscordId(SpaceUser user) + { + return (await _db.Discords.SingleOrDefaultAsync(p => p.SpaceUserId == user.Id))?.DiscordId; + } + } +} \ No newline at end of file diff --git a/SS14.Auth.Shared/StartupHelpers.cs b/SS14.Auth.Shared/StartupHelpers.cs index bf5628d..d15527b 100644 --- a/SS14.Auth.Shared/StartupHelpers.cs +++ b/SS14.Auth.Shared/StartupHelpers.cs @@ -76,6 +76,7 @@ public static void AddShared(IServiceCollection services, IConfiguration config) services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddTransient(_ => RandomNumberGenerator.Create()); diff --git a/SS14.Auth/Controllers/QueryApiController.cs b/SS14.Auth/Controllers/QueryApiController.cs index f8473a4..dbd52fc 100644 --- a/SS14.Auth/Controllers/QueryApiController.cs +++ b/SS14.Auth/Controllers/QueryApiController.cs @@ -13,11 +13,13 @@ public class QueryApiController : ControllerBase { private readonly UserManager _userManager; private readonly PatreonDataManager _patreonDataManager; + private readonly DiscordDataManager _discordDataManager; - public QueryApiController(UserManager userManager, PatreonDataManager patreonDataManager) + public QueryApiController(UserManager userManager, PatreonDataManager patreonDataManager, DiscordDataManager discordDataManager) { _userManager = userManager; _patreonDataManager = patreonDataManager; + _discordDataManager = discordDataManager; } [HttpGet("name")] @@ -38,11 +40,13 @@ public async Task QueryByUserId(Guid userId) internal static async Task BuildUserResponse( PatreonDataManager patreonDataManager, + DiscordDataManager discordDataManager, SpaceUser user) { var patronTier = await patreonDataManager.GetPatreonTierAsync(user); + var discordId = await discordDataManager.GetUserDiscordId(user); - return new QueryUserResponse(user.UserName, user.Id, patronTier, user.CreatedTime); + return new QueryUserResponse(user.UserName, user.Id, patronTier, discordId, user.CreatedTime); } private async Task DoResponse(SpaceUser? user) @@ -50,7 +54,7 @@ private async Task DoResponse(SpaceUser? user) if (user == null) return NotFound(); - return Ok(await BuildUserResponse(_patreonDataManager, user)); + return Ok(await BuildUserResponse(_patreonDataManager, _discordDataManager, user)); } } } \ No newline at end of file diff --git a/SS14.Auth/Controllers/SessionApiController.cs b/SS14.Auth/Controllers/SessionApiController.cs index c7c41a1..6240c01 100644 --- a/SS14.Auth/Controllers/SessionApiController.cs +++ b/SS14.Auth/Controllers/SessionApiController.cs @@ -22,6 +22,7 @@ public class SessionApiController : ControllerBase private readonly SpaceUserManager _userManager; private readonly ApplicationDbContext _dbContext; private readonly PatreonDataManager _patreonDataManager; + private readonly DiscordDataManager _discordDataManager; private readonly ISystemClock _clock; public SessionApiController( @@ -29,13 +30,15 @@ public SessionApiController( SpaceUserManager userManager, ApplicationDbContext dbContext, ISystemClock clock, - PatreonDataManager patreonDataManager) + PatreonDataManager patreonDataManager, + DiscordDataManager discordDataManager) { _configuration = configuration; _userManager = userManager; _dbContext = dbContext; _clock = clock; _patreonDataManager = patreonDataManager; + _discordDataManager = discordDataManager; } [Authorize(AuthenticationSchemes = "SS14Auth")] @@ -84,7 +87,7 @@ public async Task HasJoined(Guid userId, string hash) } var userResponse = await QueryApiController.BuildUserResponse( - _patreonDataManager, authHash.SpaceUser); + _patreonDataManager, _discordDataManager, authHash.SpaceUser); var resp = new HasJoinedResponse(true, userResponse); diff --git a/SS14.Auth/Responses/QueryUserResponse.cs b/SS14.Auth/Responses/QueryUserResponse.cs index 18389e9..d13b180 100644 --- a/SS14.Auth/Responses/QueryUserResponse.cs +++ b/SS14.Auth/Responses/QueryUserResponse.cs @@ -6,6 +6,7 @@ public sealed record QueryUserResponse( string UserName, Guid UserId, string? PatronTier, + string? DiscordId, DateTimeOffset CreatedTime) { } From 5ffd160fb253fab055ef21b9ee9fc2962f51c17a Mon Sep 17 00:00:00 2001 From: Morbo <14136326+Morb0@users.noreply.github.com> Date: Wed, 28 Sep 2022 07:56:44 +0300 Subject: [PATCH 3/9] Add HTTP route to get user by Discoird id --- SS14.Auth.Shared/Data/DiscordDataManager.cs | 5 +++++ SS14.Auth/Controllers/QueryApiController.cs | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/SS14.Auth.Shared/Data/DiscordDataManager.cs b/SS14.Auth.Shared/Data/DiscordDataManager.cs index 00389f8..ce41faa 100644 --- a/SS14.Auth.Shared/Data/DiscordDataManager.cs +++ b/SS14.Auth.Shared/Data/DiscordDataManager.cs @@ -16,5 +16,10 @@ public async Task GetUserDiscordId(SpaceUser user) { return (await _db.Discords.SingleOrDefaultAsync(p => p.SpaceUserId == user.Id))?.DiscordId; } + + public async Task GetUserByDiscordId(string discordId) + { + return (await _db.Discords.Include(d => d.SpaceUser).SingleOrDefaultAsync(p => p.DiscordId == discordId))?.SpaceUser; + } } } \ No newline at end of file diff --git a/SS14.Auth/Controllers/QueryApiController.cs b/SS14.Auth/Controllers/QueryApiController.cs index dbd52fc..17b535d 100644 --- a/SS14.Auth/Controllers/QueryApiController.cs +++ b/SS14.Auth/Controllers/QueryApiController.cs @@ -37,6 +37,14 @@ public async Task QueryByUserId(Guid userId) var user = await _userManager.FindByIdAsync(userId.ToString()); return await DoResponse(user); } + + [HttpGet("discord")] + [HttpHead("discord")] + public async Task QueryByDiscordId(string discordId) + { + var user = await _discordDataManager.GetUserByDiscordId(discordId); + return await DoResponse(user); + } internal static async Task BuildUserResponse( PatreonDataManager patreonDataManager, From b1d537ffb12e7aab6824f38e59c365a6278e3cf4 Mon Sep 17 00:00:00 2001 From: Morbo <14136326+Morb0@users.noreply.github.com> Date: Sat, 18 Feb 2023 17:25:59 +0300 Subject: [PATCH 4/9] Add session move action --- SS14.Web/Controllers/HomeController.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/SS14.Web/Controllers/HomeController.cs b/SS14.Web/Controllers/HomeController.cs index f33b840..4abced1 100644 --- a/SS14.Web/Controllers/HomeController.cs +++ b/SS14.Web/Controllers/HomeController.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using SS14.Web.Models; @@ -29,6 +30,23 @@ public IActionResult Contact() return Redirect("https://spacestation14.io/about/contact/"); } + public IActionResult MoveSession(string accessToken = null, string returnUrl = "/") + { + if (accessToken == null) + { + return BadRequest("A accessToken is required."); + } + + Response.Cookies.Append(".AspNetCore.Identity.Application", accessToken, new CookieOptions + { + Secure = true, + HttpOnly = true, + SameSite = SameSiteMode.None, + }); + + return LocalRedirect(returnUrl ?? "/"); + } + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { @@ -39,4 +57,4 @@ public IActionResult MainWebsite() { return Redirect("https://spacestation14.io/"); } -} \ No newline at end of file +} From ace28a1613761b12657373a49c335d64c29973d8 Mon Sep 17 00:00:00 2001 From: Morbo <14136326+Morb0@users.noreply.github.com> Date: Sat, 18 Feb 2023 18:09:29 +0300 Subject: [PATCH 5/9] Revert "Add session move action" This reverts commit b1d537ffb12e7aab6824f38e59c365a6278e3cf4. --- SS14.Web/Controllers/HomeController.cs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/SS14.Web/Controllers/HomeController.cs b/SS14.Web/Controllers/HomeController.cs index 4abced1..f33b840 100644 --- a/SS14.Web/Controllers/HomeController.cs +++ b/SS14.Web/Controllers/HomeController.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using SS14.Web.Models; @@ -30,23 +29,6 @@ public IActionResult Contact() return Redirect("https://spacestation14.io/about/contact/"); } - public IActionResult MoveSession(string accessToken = null, string returnUrl = "/") - { - if (accessToken == null) - { - return BadRequest("A accessToken is required."); - } - - Response.Cookies.Append(".AspNetCore.Identity.Application", accessToken, new CookieOptions - { - Secure = true, - HttpOnly = true, - SameSite = SameSiteMode.None, - }); - - return LocalRedirect(returnUrl ?? "/"); - } - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { @@ -57,4 +39,4 @@ public IActionResult MainWebsite() { return Redirect("https://spacestation14.io/"); } -} +} \ No newline at end of file From 775eb34eeed3b16e34d0794290c64a3f60d6047a Mon Sep 17 00:00:00 2001 From: Morbo <14136326+Morb0@users.noreply.github.com> Date: Sat, 18 Feb 2023 23:13:28 +0300 Subject: [PATCH 6/9] Add Discord linking in Auth API --- .../Config/DiscordConfiguration.cs | 3 +- SS14.Auth.Shared/Data/ApplicationDbContext.cs | 1 + SS14.Auth.Shared/Data/Discord.cs | 17 +- SS14.Auth.Shared/Data/DiscordDataManager.cs | 33 +- SS14.Auth.Shared/Data/DiscordLoginSession.cs | 13 + .../Data/DiscordLoginSessionManager.cs | 123 ++ ...0218171536_DiscordLoginSession.Designer.cs | 1725 +++++++++++++++++ .../20230218171536_DiscordLoginSession.cs | 43 + .../ApplicationDbContextModelSnapshot.cs | 30 + SS14.Auth.Shared/StartupHelpers.cs | 3 +- .../Controllers/DiscordLinkController.cs | 53 + SS14.Auth/Startup.cs | 4 +- 12 files changed, 2019 insertions(+), 29 deletions(-) create mode 100644 SS14.Auth.Shared/Data/DiscordLoginSession.cs create mode 100644 SS14.Auth.Shared/Data/DiscordLoginSessionManager.cs create mode 100644 SS14.Auth.Shared/Data/Migrations/20230218171536_DiscordLoginSession.Designer.cs create mode 100644 SS14.Auth.Shared/Data/Migrations/20230218171536_DiscordLoginSession.cs create mode 100644 SS14.Auth/Controllers/DiscordLinkController.cs diff --git a/SS14.Auth.Shared/Config/DiscordConfiguration.cs b/SS14.Auth.Shared/Config/DiscordConfiguration.cs index 948f81b..06acea6 100644 --- a/SS14.Auth.Shared/Config/DiscordConfiguration.cs +++ b/SS14.Auth.Shared/Config/DiscordConfiguration.cs @@ -4,5 +4,6 @@ public sealed class DiscordConfiguration { public string ClientId { get; set; } public string ClientSecret { get; set; } + public string RedirectUri { get; set; } } -} \ No newline at end of file +} diff --git a/SS14.Auth.Shared/Data/ApplicationDbContext.cs b/SS14.Auth.Shared/Data/ApplicationDbContext.cs index aeda17c..fccaa25 100644 --- a/SS14.Auth.Shared/Data/ApplicationDbContext.cs +++ b/SS14.Auth.Shared/Data/ApplicationDbContext.cs @@ -122,6 +122,7 @@ protected override void OnModelCreating(ModelBuilder builder) public DbSet PastAccountNames { get; set; } public DbSet AccountLogs { get; set; } public DbSet Discords { get; set; } + public DbSet DiscordLoginSessions { get; set; } // IS4 configuration. public DbSet Clients { get; set; } diff --git a/SS14.Auth.Shared/Data/Discord.cs b/SS14.Auth.Shared/Data/Discord.cs index eb74543..a9b8bcf 100644 --- a/SS14.Auth.Shared/Data/Discord.cs +++ b/SS14.Auth.Shared/Data/Discord.cs @@ -1,14 +1,13 @@ using System; using System.ComponentModel.DataAnnotations; -namespace SS14.Auth.Shared.Data +namespace SS14.Auth.Shared.Data; + +public class Discord { - public class Discord - { - public int Id { get; set; } + public int Id { get; set; } - [Required] public string DiscordId { get; set; } - [Required] public Guid SpaceUserId { get; set; } - public SpaceUser SpaceUser { get; set; } - } -} \ No newline at end of file + [Required] public string DiscordId { get; set; } + [Required] public Guid SpaceUserId { get; set; } + public SpaceUser SpaceUser { get; set; } +} diff --git a/SS14.Auth.Shared/Data/DiscordDataManager.cs b/SS14.Auth.Shared/Data/DiscordDataManager.cs index ce41faa..f1d0552 100644 --- a/SS14.Auth.Shared/Data/DiscordDataManager.cs +++ b/SS14.Auth.Shared/Data/DiscordDataManager.cs @@ -1,25 +1,24 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; -namespace SS14.Auth.Shared.Data +namespace SS14.Auth.Shared.Data; + +public sealed class DiscordDataManager { - public sealed class DiscordDataManager - { - private readonly ApplicationDbContext _db; + private readonly ApplicationDbContext _db; - public DiscordDataManager(ApplicationDbContext db) - { - _db = db; - } + public DiscordDataManager(ApplicationDbContext db) + { + _db = db; + } - public async Task GetUserDiscordId(SpaceUser user) - { - return (await _db.Discords.SingleOrDefaultAsync(p => p.SpaceUserId == user.Id))?.DiscordId; - } + public async Task GetUserDiscordId(SpaceUser user) + { + return (await _db.Discords.SingleOrDefaultAsync(p => p.SpaceUserId == user.Id))?.DiscordId; + } - public async Task GetUserByDiscordId(string discordId) - { - return (await _db.Discords.Include(d => d.SpaceUser).SingleOrDefaultAsync(p => p.DiscordId == discordId))?.SpaceUser; - } + public async Task GetUserByDiscordId(string discordId) + { + return (await _db.Discords.Include(d => d.SpaceUser).SingleOrDefaultAsync(p => p.DiscordId == discordId))?.SpaceUser; } -} \ No newline at end of file +} diff --git a/SS14.Auth.Shared/Data/DiscordLoginSession.cs b/SS14.Auth.Shared/Data/DiscordLoginSession.cs new file mode 100644 index 0000000..3675b7b --- /dev/null +++ b/SS14.Auth.Shared/Data/DiscordLoginSession.cs @@ -0,0 +1,13 @@ +using System; + +namespace SS14.Auth.Shared.Data; + +public class DiscordLoginSession +{ + public Guid Id { get; set; } + + public Guid SpaceUserId { get; set; } + public SpaceUser SpaceUser { get; set; } + + public DateTimeOffset Expires { get; set; } +} diff --git a/SS14.Auth.Shared/Data/DiscordLoginSessionManager.cs b/SS14.Auth.Shared/Data/DiscordLoginSessionManager.cs new file mode 100644 index 0000000..5342b44 --- /dev/null +++ b/SS14.Auth.Shared/Data/DiscordLoginSessionManager.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using SS14.Auth.Shared.Config; + +namespace SS14.Auth.Shared.Data; + +public sealed class DiscordLoginSessionManager +{ + public static readonly TimeSpan DefaultExpireTime = TimeSpan.FromMinutes(5); + + private readonly ApplicationDbContext _db; + private readonly ISystemClock _clock; + private readonly HttpClient _httpClient; + private readonly IOptions _config; + private readonly ILogger _logger; + + public DiscordLoginSessionManager( + ApplicationDbContext db, + ISystemClock clock, + IHttpClientFactory httpClientFactory, + IOptions config, + ILogger logger) + { + _db = db; + _clock = clock; + _httpClient = httpClientFactory.CreateClient(nameof(DiscordLoginSessionManager)); + _config = config; + _logger = logger; + } + + public async Task RegisterNewSession(SpaceUser user, TimeSpan expireTime) + { + var expiresAt = _clock.UtcNow + expireTime; + var session = new DiscordLoginSession + { + SpaceUserId = user.Id, + Expires = expiresAt, + }; + _db.DiscordLoginSessions.Add(session); + await _db.SaveChangesAsync(); + return session; + } + + public async Task GetSessionById(Guid sessionId) + { + var session = await _db.DiscordLoginSessions + .Include(p => p.SpaceUser) + .Include(p => p.SpaceUser.Discord) + .SingleOrDefaultAsync(s => s.Id == sessionId); + + if (session == null) + { + // Session does not exist. + return null; + } + + if (session.Expires < _clock.UtcNow) + { + // Token expired. + return null; + } + + return session.SpaceUser; + } + + public async Task LinkDiscord(SpaceUser user, string discordCode) + { + var accessToken = await ExchangeDiscordCode(discordCode); + var discordId = await GetDiscordId(accessToken); + user.Discord = new Discord + { + DiscordId = discordId, + }; + await _db.SaveChangesAsync(); + _logger.LogInformation("User {UserId} linked to {DiscordId} Discord", user.Id, discordId); + } + + private async Task ExchangeDiscordCode(string discordCode) + { + var config = _config.Value; + + var exchangeParams = new List>(5) + { + new("client_id", config.ClientId), + new("client_secret", config.ClientSecret), + new("redirect_uri", config.RedirectUri), + new("grant_type", "authorization_code"), + new("code", discordCode), + }; + var form = new FormUrlEncodedContent(exchangeParams); + var resp = await _httpClient.PostAsync("https://discord.com/api/v10/oauth2/token", form); + var data = await resp.Content.ReadFromJsonAsync(); + resp.EnsureSuccessStatusCode(); + return data.AccessToken; + } + + private async Task GetDiscordId(string accessToken) + { + var request = new HttpRequestMessage(HttpMethod.Get, "https://discord.com/api/v10/users/@me"); + request.Headers.Add("Authorization", $"Bearer {accessToken}"); + var resp = await _httpClient.SendAsync(request); + resp.EnsureSuccessStatusCode(); + var data = await resp.Content.ReadFromJsonAsync(); + return data.Id; + } + + private sealed record DiscordExchangeResponse( + [property: JsonPropertyName("access_token")] string AccessToken + ); + + private sealed record DiscordMeResponse( + [property: JsonPropertyName("id")] string Id + ); +} + diff --git a/SS14.Auth.Shared/Data/Migrations/20230218171536_DiscordLoginSession.Designer.cs b/SS14.Auth.Shared/Data/Migrations/20230218171536_DiscordLoginSession.Designer.cs new file mode 100644 index 0000000..4a8a545 --- /dev/null +++ b/SS14.Auth.Shared/Data/Migrations/20230218171536_DiscordLoginSession.Designer.cs @@ -0,0 +1,1725 @@ +// +using System; +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using SS14.Auth.Shared.Data; + +#nullable disable + +namespace SS14.Auth.Shared.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20230218171536_DiscordLoginSession")] + partial class DiscordLoginSession + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AllowedAccessTokenSigningAlgorithms") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("LastAccessed") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("NonEditable") + .HasColumnType("boolean"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.Property("Updated") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ApiResources", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApiResourceId") + .HasColumnType("integer"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("ApiResourceId"); + + b.ToTable("ApiResourceClaims", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApiResourceId") + .HasColumnType("integer"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("Id"); + + b.HasIndex("ApiResourceId"); + + b.ToTable("ApiResourceProperties", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApiResourceId") + .HasColumnType("integer"); + + b.Property("Scope") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("ApiResourceId"); + + b.ToTable("ApiResourceScopes", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApiResourceId") + .HasColumnType("integer"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.HasKey("Id"); + + b.HasIndex("ApiResourceId"); + + b.ToTable("ApiResourceSecrets", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Emphasize") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ApiScopes", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ScopeId") + .HasColumnType("integer"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("ScopeId"); + + b.ToTable("ApiScopeClaims", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("ScopeId") + .HasColumnType("integer"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("Id"); + + b.HasIndex("ScopeId"); + + b.ToTable("ApiScopeProperties", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AbsoluteRefreshTokenLifetime") + .HasColumnType("integer"); + + b.Property("AccessTokenLifetime") + .HasColumnType("integer"); + + b.Property("AccessTokenType") + .HasColumnType("integer"); + + b.Property("AllowAccessTokensViaBrowser") + .HasColumnType("boolean"); + + b.Property("AllowOfflineAccess") + .HasColumnType("boolean"); + + b.Property("AllowPlainTextPkce") + .HasColumnType("boolean"); + + b.Property("AllowRememberConsent") + .HasColumnType("boolean"); + + b.Property("AllowedIdentityTokenSigningAlgorithms") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("AlwaysIncludeUserClaimsInIdToken") + .HasColumnType("boolean"); + + b.Property("AlwaysSendClientClaims") + .HasColumnType("boolean"); + + b.Property("AuthorizationCodeLifetime") + .HasColumnType("integer"); + + b.Property("BackChannelLogoutSessionRequired") + .HasColumnType("boolean"); + + b.Property("BackChannelLogoutUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ClientClaimsPrefix") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ConsentLifetime") + .HasColumnType("integer"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DeviceCodeLifetime") + .HasColumnType("integer"); + + b.Property("EnableLocalLogin") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("FrontChannelLogoutSessionRequired") + .HasColumnType("boolean"); + + b.Property("FrontChannelLogoutUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("IdentityTokenLifetime") + .HasColumnType("integer"); + + b.Property("IncludeJwtId") + .HasColumnType("boolean"); + + b.Property("LastAccessed") + .HasColumnType("timestamp with time zone"); + + b.Property("LogoUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("NonEditable") + .HasColumnType("boolean"); + + b.Property("PairWiseSubjectSalt") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ProtocolType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RefreshTokenExpiration") + .HasColumnType("integer"); + + b.Property("RefreshTokenUsage") + .HasColumnType("integer"); + + b.Property("RequireClientSecret") + .HasColumnType("boolean"); + + b.Property("RequireConsent") + .HasColumnType("boolean"); + + b.Property("RequirePkce") + .HasColumnType("boolean"); + + b.Property("RequireRequestObject") + .HasColumnType("boolean"); + + b.Property("SlidingRefreshTokenLifetime") + .HasColumnType("integer"); + + b.Property("UpdateAccessTokenClaimsOnRefresh") + .HasColumnType("boolean"); + + b.Property("Updated") + .HasColumnType("timestamp with time zone"); + + b.Property("UserCodeType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UserSsoLifetime") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("Clients", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientClaims", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Origin") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientCorsOrigins", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("GrantType") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientGrantTypes", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientIdPRestrictions", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("PostLogoutRedirectUri") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientPostLogoutRedirectUris", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientProperties", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("RedirectUri") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientRedirectUris", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Scope") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientScopes", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ClientSecrets", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => + { + b.Property("UserCode") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("character varying(50000)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Expiration") + .IsRequired() + .HasColumnType("timestamp with time zone"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("UserCode"); + + b.HasIndex("DeviceCode") + .IsUnique(); + + b.HasIndex("Expiration"); + + b.ToTable("DeviceCodes", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Emphasize") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("NonEditable") + .HasColumnType("boolean"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.Property("Updated") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("IdentityResources", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IdentityResourceId") + .HasColumnType("integer"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityResourceId"); + + b.ToTable("IdentityResourceClaims", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IdentityResourceId") + .HasColumnType("integer"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityResourceId"); + + b.ToTable("IdentityResourceProperties", "IS4"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConsumedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("character varying(50000)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.HasIndex("Expiration"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.HasIndex("SubjectId", "SessionId", "Type"); + + b.ToTable("PersistedGrants", "IS4"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.AccountLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Data") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.Property("Time") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SpaceUserId"); + + b.ToTable("AccountLogs"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.AuthHash", b => + { + b.Property("AuthHashId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("AuthHashId")); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.HasKey("AuthHashId"); + + b.HasIndex("SpaceUserId"); + + b.HasIndex("Hash", "SpaceUserId") + .IsUnique(); + + b.ToTable("AuthHashes"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.BurnerEmail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Domain") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Domain") + .IsUnique(); + + b.ToTable("BurnerEmails"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.Discord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DiscordId") + .IsRequired() + .HasColumnType("text"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DiscordId") + .IsUnique(); + + b.HasIndex("SpaceUserId") + .IsUnique(); + + b.ToTable("Discords"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.DiscordLoginSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SpaceUserId"); + + b.ToTable("DiscordLoginSessions"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.LoginSession", b => + { + b.Property("LoginSessionId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("LoginSessionId")); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.Property("Token") + .IsRequired() + .HasColumnType("bytea"); + + b.HasKey("LoginSessionId"); + + b.HasIndex("SpaceUserId"); + + b.HasIndex("Token") + .IsUnique(); + + b.ToTable("ActiveSessions"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.PastAccountName", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChangeTime") + .HasColumnType("timestamp with time zone"); + + b.Property("PastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SpaceUserId"); + + b.ToTable("PastAccountNames"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.PatreonWebhookLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Content") + .HasColumnType("jsonb"); + + b.Property("Time") + .HasColumnType("timestamp with time zone"); + + b.Property("Trigger") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("PatreonWebhookLogs"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.Patron", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CurrentTier") + .HasColumnType("text"); + + b.Property("PatreonId") + .IsRequired() + .HasColumnType("text"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PatreonId") + .IsUnique(); + + b.HasIndex("SpaceUserId") + .IsUnique(); + + b.ToTable("Patrons"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.SpaceRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.SpaceUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.UserOAuthClient", b => + { + b.Property("UserOAuthClientId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("UserOAuthClientId")); + + b.Property("ClientId") + .HasColumnType("integer"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.HasKey("UserOAuthClientId"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.HasIndex("SpaceUserId"); + + b.ToTable("UserOAuthClients"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiResource"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") + .WithMany("Properties") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiResource"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") + .WithMany("Scopes") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiResource"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") + .WithMany("Secrets") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiResource"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "Scope") + .WithMany("UserClaims") + .HasForeignKey("ScopeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Scope"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "Scope") + .WithMany("Properties") + .HasForeignKey("ScopeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Scope"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("Claims") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("AllowedCorsOrigins") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("AllowedGrantTypes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("IdentityProviderRestrictions") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("PostLogoutRedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("Properties") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("RedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("AllowedScopes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany("ClientSecrets") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") + .WithMany("UserClaims") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityResource"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") + .WithMany("Properties") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityResource"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.AccountLog", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithMany("AccountLogs") + .HasForeignKey("SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpaceUser"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.AuthHash", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithMany("AuthHashes") + .HasForeignKey("SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpaceUser"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.Discord", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithOne("Discord") + .HasForeignKey("SS14.Auth.Shared.Data.Discord", "SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpaceUser"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.DiscordLoginSession", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithMany() + .HasForeignKey("SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpaceUser"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.LoginSession", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithMany("LoginSessions") + .HasForeignKey("SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpaceUser"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.PastAccountName", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithMany("PastAccountNames") + .HasForeignKey("SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpaceUser"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.Patron", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithOne("Patron") + .HasForeignKey("SS14.Auth.Shared.Data.Patron", "SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpaceUser"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.UserOAuthClient", b => + { + b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithMany() + .HasForeignKey("SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + + b.Navigation("SpaceUser"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b => + { + b.Navigation("Properties"); + + b.Navigation("Scopes"); + + b.Navigation("Secrets"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => + { + b.Navigation("Properties"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b => + { + b.Navigation("AllowedCorsOrigins"); + + b.Navigation("AllowedGrantTypes"); + + b.Navigation("AllowedScopes"); + + b.Navigation("Claims"); + + b.Navigation("ClientSecrets"); + + b.Navigation("IdentityProviderRestrictions"); + + b.Navigation("PostLogoutRedirectUris"); + + b.Navigation("Properties"); + + b.Navigation("RedirectUris"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b => + { + b.Navigation("Properties"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("SS14.Auth.Shared.Data.SpaceUser", b => + { + b.Navigation("AccountLogs"); + + b.Navigation("AuthHashes"); + + b.Navigation("Discord"); + + b.Navigation("LoginSessions"); + + b.Navigation("PastAccountNames"); + + b.Navigation("Patron"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SS14.Auth.Shared/Data/Migrations/20230218171536_DiscordLoginSession.cs b/SS14.Auth.Shared/Data/Migrations/20230218171536_DiscordLoginSession.cs new file mode 100644 index 0000000..86aef31 --- /dev/null +++ b/SS14.Auth.Shared/Data/Migrations/20230218171536_DiscordLoginSession.cs @@ -0,0 +1,43 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SS14.Auth.Shared.Data.Migrations +{ + public partial class DiscordLoginSession : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "DiscordLoginSessions", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + SpaceUserId = table.Column(type: "uuid", nullable: false), + Expires = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_DiscordLoginSessions", x => x.Id); + table.ForeignKey( + name: "FK_DiscordLoginSessions_AspNetUsers_SpaceUserId", + column: x => x.SpaceUserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_DiscordLoginSessions_SpaceUserId", + table: "DiscordLoginSessions", + column: "SpaceUserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "DiscordLoginSessions"); + } + } +} diff --git a/SS14.Auth.Shared/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/SS14.Auth.Shared/Data/Migrations/ApplicationDbContextModelSnapshot.cs index e39bdcc..48c51a9 100644 --- a/SS14.Auth.Shared/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/SS14.Auth.Shared/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1096,6 +1096,25 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Discords"); }); + modelBuilder.Entity("SS14.Auth.Shared.Data.DiscordLoginSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("SpaceUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SpaceUserId"); + + b.ToTable("DiscordLoginSessions"); + }); + modelBuilder.Entity("SS14.Auth.Shared.Data.LoginSession", b => { b.Property("LoginSessionId") @@ -1575,6 +1594,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("SpaceUser"); }); + modelBuilder.Entity("SS14.Auth.Shared.Data.DiscordLoginSession", b => + { + b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") + .WithMany() + .HasForeignKey("SpaceUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpaceUser"); + }); + modelBuilder.Entity("SS14.Auth.Shared.Data.LoginSession", b => { b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") diff --git a/SS14.Auth.Shared/StartupHelpers.cs b/SS14.Auth.Shared/StartupHelpers.cs index 4308151..215f551 100644 --- a/SS14.Auth.Shared/StartupHelpers.cs +++ b/SS14.Auth.Shared/StartupHelpers.cs @@ -76,7 +76,8 @@ public static void AddShared(IServiceCollection services, IConfiguration config) services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddTransient(_ => RandomNumberGenerator.Create()); diff --git a/SS14.Auth/Controllers/DiscordLinkController.cs b/SS14.Auth/Controllers/DiscordLinkController.cs new file mode 100644 index 0000000..5206c79 --- /dev/null +++ b/SS14.Auth/Controllers/DiscordLinkController.cs @@ -0,0 +1,53 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using SS14.Auth.Shared.Data; + +namespace SS14.Auth.Controllers; + +[ApiController] +[Route("/api/discord")] +public class DiscordLinkController : ControllerBase +{ + private readonly SpaceUserManager _userManager; + private readonly DiscordLoginSessionManager _loginSessionManager; + + public DiscordLinkController( + SpaceUserManager userManager, + DiscordLoginSessionManager loginSessionManager) + { + _userManager = userManager; + _loginSessionManager = loginSessionManager; + } + + [Authorize(AuthenticationSchemes = "SS14Auth")] + [HttpPost("session")] + public async Task Generate() + { + var user = await _userManager.GetUserAsync(User); + var session = await _loginSessionManager.RegisterNewSession(user, DiscordLoginSessionManager.DefaultExpireTime); + return Ok(new DiscordSessionResponse(session.Id, session.Expires)); + } + + [HttpGet("callback")] + public async Task Callback(Guid state, string code) + { + var user = await _loginSessionManager.GetSessionById(state); + + if (user == null) + return NotFound("Session not exist or expired"); + + if (user.Discord != null) + return BadRequest("⚠️ You already linked Discord with you account.\nAccount can be unlinked in account settings."); + + await _loginSessionManager.LinkDiscord(user, code); + + return Ok("✅ Discord successfully linked to your account!\nYou can now close this page and return to launcher."); + } +} + +public sealed record DiscordSessionResponse(Guid SessionId, DateTimeOffset ExpireTime) +{ +} diff --git a/SS14.Auth/Startup.cs b/SS14.Auth/Startup.cs index 7c29f9a..f4f91a5 100644 --- a/SS14.Auth/Startup.cs +++ b/SS14.Auth/Startup.cs @@ -9,6 +9,7 @@ using SS14.Auth.Services; using SS14.Auth.Shared; using SS14.Auth.Shared.Auth; +using SS14.Auth.Shared.Data; namespace SS14.Auth; @@ -38,6 +39,7 @@ public void ConfigureServices(IServiceCollection services) .AddScheme("SS14Auth", _ => {}); services.AddHostedService(); + services.AddHttpClient(nameof(DiscordLoginSessionManager)); StartupHelpers.AddShared(services, Configuration); } @@ -69,4 +71,4 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) endpoints.MapMetrics(); }); } -} \ No newline at end of file +} From e87a09a5b640f158c5e0f37015b4dd7a24657c37 Mon Sep 17 00:00:00 2001 From: Morbo <14136326+Morb0@users.noreply.github.com> Date: Sat, 18 Feb 2023 23:28:41 +0300 Subject: [PATCH 7/9] Add null validation --- SS14.Auth.Shared/Data/DiscordLoginSessionManager.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/SS14.Auth.Shared/Data/DiscordLoginSessionManager.cs b/SS14.Auth.Shared/Data/DiscordLoginSessionManager.cs index 5342b44..160580a 100644 --- a/SS14.Auth.Shared/Data/DiscordLoginSessionManager.cs +++ b/SS14.Auth.Shared/Data/DiscordLoginSessionManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Net.Http; using System.Net.Http.Json; using System.Text.Json.Serialization; @@ -97,8 +98,12 @@ private async Task ExchangeDiscordCode(string discordCode) }; var form = new FormUrlEncodedContent(exchangeParams); var resp = await _httpClient.PostAsync("https://discord.com/api/v10/oauth2/token", form); - var data = await resp.Content.ReadFromJsonAsync(); resp.EnsureSuccessStatusCode(); + + var data = await resp.Content.ReadFromJsonAsync(); + if (data == null) + throw new InvalidDataException("Response data cannot be null"); + return data.AccessToken; } @@ -108,7 +113,11 @@ private async Task GetDiscordId(string accessToken) request.Headers.Add("Authorization", $"Bearer {accessToken}"); var resp = await _httpClient.SendAsync(request); resp.EnsureSuccessStatusCode(); + var data = await resp.Content.ReadFromJsonAsync(); + if (data == null) + throw new InvalidDataException("Response data cannot be null"); + return data.Id; } From b1f5a5835ae1f9b97f51c0a29b7234ea93240f13 Mon Sep 17 00:00:00 2001 From: Morbo <14136326+Morb0@users.noreply.github.com> Date: Sat, 18 Feb 2023 23:58:26 +0300 Subject: [PATCH 8/9] Use simple filed for user instead of separate table & regenerate migrations --- SS14.Auth.Shared/Data/ApplicationDbContext.cs | 9 - SS14.Auth.Shared/Data/Discord.cs | 13 - SS14.Auth.Shared/Data/DiscordDataManager.cs | 7 +- .../Data/DiscordLoginSessionManager.cs | 6 +- .../20220928002800_Discord.Designer.cs | 1612 ----------------- .../Data/Migrations/20220928002800_Discord.cs | 52 - ...20230218205029_DiscordLinking.Designer.cs} | 46 +- ...on.cs => 20230218205029_DiscordLinking.cs} | 12 +- .../ApplicationDbContextModelSnapshot.cs | 42 +- SS14.Auth.Shared/Data/SpaceUser.cs | 2 +- .../Controllers/DiscordLinkController.cs | 2 +- SS14.Auth/Controllers/QueryApiController.cs | 3 +- .../Account/Manage/ManageDiscord.cshtml.cs | 19 +- SS14.Web/DiscordConnectionHandler.cs | 26 +- 14 files changed, 35 insertions(+), 1816 deletions(-) delete mode 100644 SS14.Auth.Shared/Data/Discord.cs delete mode 100644 SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.Designer.cs delete mode 100644 SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.cs rename SS14.Auth.Shared/Data/Migrations/{20230218171536_DiscordLoginSession.Designer.cs => 20230218205029_DiscordLinking.Designer.cs} (97%) rename SS14.Auth.Shared/Data/Migrations/{20230218171536_DiscordLoginSession.cs => 20230218205029_DiscordLinking.cs} (81%) diff --git a/SS14.Auth.Shared/Data/ApplicationDbContext.cs b/SS14.Auth.Shared/Data/ApplicationDbContext.cs index fccaa25..f451317 100644 --- a/SS14.Auth.Shared/Data/ApplicationDbContext.cs +++ b/SS14.Auth.Shared/Data/ApplicationDbContext.cs @@ -70,14 +70,6 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasIndex(p => new { p.ClientId }) .IsUnique(); - - builder.Entity() - .HasIndex(p => p.DiscordId) - .IsUnique(); - - builder.Entity() - .HasIndex(p => p.SpaceUserId) - .IsUnique(); var cfgStoreOptions = new ConfigurationStoreOptions { @@ -121,7 +113,6 @@ protected override void OnModelCreating(ModelBuilder builder) public DbSet UserOAuthClients { get; set; } public DbSet PastAccountNames { get; set; } public DbSet AccountLogs { get; set; } - public DbSet Discords { get; set; } public DbSet DiscordLoginSessions { get; set; } // IS4 configuration. diff --git a/SS14.Auth.Shared/Data/Discord.cs b/SS14.Auth.Shared/Data/Discord.cs deleted file mode 100644 index a9b8bcf..0000000 --- a/SS14.Auth.Shared/Data/Discord.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; - -namespace SS14.Auth.Shared.Data; - -public class Discord -{ - public int Id { get; set; } - - [Required] public string DiscordId { get; set; } - [Required] public Guid SpaceUserId { get; set; } - public SpaceUser SpaceUser { get; set; } -} diff --git a/SS14.Auth.Shared/Data/DiscordDataManager.cs b/SS14.Auth.Shared/Data/DiscordDataManager.cs index f1d0552..b81295c 100644 --- a/SS14.Auth.Shared/Data/DiscordDataManager.cs +++ b/SS14.Auth.Shared/Data/DiscordDataManager.cs @@ -12,13 +12,8 @@ public DiscordDataManager(ApplicationDbContext db) _db = db; } - public async Task GetUserDiscordId(SpaceUser user) - { - return (await _db.Discords.SingleOrDefaultAsync(p => p.SpaceUserId == user.Id))?.DiscordId; - } - public async Task GetUserByDiscordId(string discordId) { - return (await _db.Discords.Include(d => d.SpaceUser).SingleOrDefaultAsync(p => p.DiscordId == discordId))?.SpaceUser; + return (await _db.Users.SingleOrDefaultAsync(p => p.DiscordId == discordId)); } } diff --git a/SS14.Auth.Shared/Data/DiscordLoginSessionManager.cs b/SS14.Auth.Shared/Data/DiscordLoginSessionManager.cs index 160580a..581ad25 100644 --- a/SS14.Auth.Shared/Data/DiscordLoginSessionManager.cs +++ b/SS14.Auth.Shared/Data/DiscordLoginSessionManager.cs @@ -54,7 +54,6 @@ public async Task GetSessionById(Guid sessionId) { var session = await _db.DiscordLoginSessions .Include(p => p.SpaceUser) - .Include(p => p.SpaceUser.Discord) .SingleOrDefaultAsync(s => s.Id == sessionId); if (session == null) @@ -76,10 +75,7 @@ public async Task LinkDiscord(SpaceUser user, string discordCode) { var accessToken = await ExchangeDiscordCode(discordCode); var discordId = await GetDiscordId(accessToken); - user.Discord = new Discord - { - DiscordId = discordId, - }; + user.DiscordId = discordId; await _db.SaveChangesAsync(); _logger.LogInformation("User {UserId} linked to {DiscordId} Discord", user.Id, discordId); } diff --git a/SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.Designer.cs b/SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.Designer.cs deleted file mode 100644 index 400797f..0000000 --- a/SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.Designer.cs +++ /dev/null @@ -1,1612 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using SS14.Auth.Shared.Data; - -#nullable disable - -namespace SS14.Auth.Shared.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20220928002800_Discord")] - partial class Discord - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "6.0.1") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AllowedAccessTokenSigningAlgorithms") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("LastAccessed") - .HasColumnType("timestamp with time zone"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("NonEditable") - .HasColumnType("boolean"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("boolean"); - - b.Property("Updated") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique(); - - b.ToTable("ApiResources", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.ToTable("ApiResourceClaims", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("Key") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.ToTable("ApiResourceProperties", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("Scope") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.ToTable("ApiResourceScopes", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(4000) - .HasColumnType("character varying(4000)"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.ToTable("ApiResourceSecrets", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("Emphasize") - .HasColumnType("boolean"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("Required") - .HasColumnType("boolean"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique(); - - b.ToTable("ApiScopes", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ScopeId") - .HasColumnType("integer"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.HasKey("Id"); - - b.HasIndex("ScopeId"); - - b.ToTable("ApiScopeClaims", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Key") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.Property("ScopeId") - .HasColumnType("integer"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.HasKey("Id"); - - b.HasIndex("ScopeId"); - - b.ToTable("ApiScopeProperties", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AbsoluteRefreshTokenLifetime") - .HasColumnType("integer"); - - b.Property("AccessTokenLifetime") - .HasColumnType("integer"); - - b.Property("AccessTokenType") - .HasColumnType("integer"); - - b.Property("AllowAccessTokensViaBrowser") - .HasColumnType("boolean"); - - b.Property("AllowOfflineAccess") - .HasColumnType("boolean"); - - b.Property("AllowPlainTextPkce") - .HasColumnType("boolean"); - - b.Property("AllowRememberConsent") - .HasColumnType("boolean"); - - b.Property("AllowedIdentityTokenSigningAlgorithms") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("AlwaysIncludeUserClaimsInIdToken") - .HasColumnType("boolean"); - - b.Property("AlwaysSendClientClaims") - .HasColumnType("boolean"); - - b.Property("AuthorizationCodeLifetime") - .HasColumnType("integer"); - - b.Property("BackChannelLogoutSessionRequired") - .HasColumnType("boolean"); - - b.Property("BackChannelLogoutUri") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.Property("ClientClaimsPrefix") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ClientId") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ClientName") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ClientUri") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.Property("ConsentLifetime") - .HasColumnType("integer"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("DeviceCodeLifetime") - .HasColumnType("integer"); - - b.Property("EnableLocalLogin") - .HasColumnType("boolean"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("FrontChannelLogoutSessionRequired") - .HasColumnType("boolean"); - - b.Property("FrontChannelLogoutUri") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.Property("IdentityTokenLifetime") - .HasColumnType("integer"); - - b.Property("IncludeJwtId") - .HasColumnType("boolean"); - - b.Property("LastAccessed") - .HasColumnType("timestamp with time zone"); - - b.Property("LogoUri") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.Property("NonEditable") - .HasColumnType("boolean"); - - b.Property("PairWiseSubjectSalt") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ProtocolType") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("RefreshTokenExpiration") - .HasColumnType("integer"); - - b.Property("RefreshTokenUsage") - .HasColumnType("integer"); - - b.Property("RequireClientSecret") - .HasColumnType("boolean"); - - b.Property("RequireConsent") - .HasColumnType("boolean"); - - b.Property("RequirePkce") - .HasColumnType("boolean"); - - b.Property("RequireRequestObject") - .HasColumnType("boolean"); - - b.Property("SlidingRefreshTokenLifetime") - .HasColumnType("integer"); - - b.Property("UpdateAccessTokenClaimsOnRefresh") - .HasColumnType("boolean"); - - b.Property("Updated") - .HasColumnType("timestamp with time zone"); - - b.Property("UserCodeType") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("UserSsoLifetime") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ClientId") - .IsUnique(); - - b.ToTable("Clients", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientClaims", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("Origin") - .IsRequired() - .HasMaxLength(150) - .HasColumnType("character varying(150)"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientCorsOrigins", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("GrantType") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientGrantTypes", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("Provider") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientIdPRestrictions", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("PostLogoutRedirectUri") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientPostLogoutRedirectUris", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("Key") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientProperties", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("RedirectUri") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientRedirectUris", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("Scope") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientScopes", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(4000) - .HasColumnType("character varying(4000)"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientSecrets", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => - { - b.Property("UserCode") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ClientId") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("CreationTime") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .IsRequired() - .HasMaxLength(50000) - .HasColumnType("character varying(50000)"); - - b.Property("Description") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("DeviceCode") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("Expiration") - .IsRequired() - .HasColumnType("timestamp with time zone"); - - b.Property("SessionId") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("SubjectId") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.HasKey("UserCode"); - - b.HasIndex("DeviceCode") - .IsUnique(); - - b.HasIndex("Expiration"); - - b.ToTable("DeviceCodes", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasMaxLength(1000) - .HasColumnType("character varying(1000)"); - - b.Property("DisplayName") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("Emphasize") - .HasColumnType("boolean"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("NonEditable") - .HasColumnType("boolean"); - - b.Property("Required") - .HasColumnType("boolean"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("boolean"); - - b.Property("Updated") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique(); - - b.ToTable("IdentityResources", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("IdentityResourceId") - .HasColumnType("integer"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityResourceId"); - - b.ToTable("IdentityResourceClaims", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("IdentityResourceId") - .HasColumnType("integer"); - - b.Property("Key") - .IsRequired() - .HasMaxLength(250) - .HasColumnType("character varying(250)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityResourceId"); - - b.ToTable("IdentityResourceProperties", "IS4"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => - { - b.Property("Key") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ClientId") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ConsumedTime") - .HasColumnType("timestamp with time zone"); - - b.Property("CreationTime") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .IsRequired() - .HasMaxLength(50000) - .HasColumnType("character varying(50000)"); - - b.Property("Description") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("SessionId") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("SubjectId") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)"); - - b.HasKey("Key"); - - b.HasIndex("Expiration"); - - b.HasIndex("SubjectId", "ClientId", "Type"); - - b.HasIndex("SubjectId", "SessionId", "Type"); - - b.ToTable("PersistedGrants", "IS4"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("text"); - - b.Property("Xml") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("DataProtectionKeys"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("ProviderKey") - .HasColumnType("text"); - - b.Property("ProviderDisplayName") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens", (string)null); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.AuthHash", b => - { - b.Property("AuthHashId") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("AuthHashId")); - - b.Property("Expires") - .HasColumnType("timestamp with time zone"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("bytea"); - - b.Property("SpaceUserId") - .HasColumnType("uuid"); - - b.HasKey("AuthHashId"); - - b.HasIndex("SpaceUserId"); - - b.HasIndex("Hash", "SpaceUserId") - .IsUnique(); - - b.ToTable("AuthHashes"); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.BurnerEmail", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Domain") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("Domain") - .IsUnique(); - - b.ToTable("BurnerEmails"); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.Discord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DiscordId") - .IsRequired() - .HasColumnType("text"); - - b.Property("SpaceUserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("DiscordId") - .IsUnique(); - - b.HasIndex("SpaceUserId") - .IsUnique(); - - b.ToTable("Discords"); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.LoginSession", b => - { - b.Property("LoginSessionId") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("LoginSessionId")); - - b.Property("Expires") - .HasColumnType("timestamp with time zone"); - - b.Property("SpaceUserId") - .HasColumnType("uuid"); - - b.Property("Token") - .IsRequired() - .HasColumnType("bytea"); - - b.HasKey("LoginSessionId"); - - b.HasIndex("SpaceUserId"); - - b.HasIndex("Token") - .IsUnique(); - - b.ToTable("ActiveSessions"); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.PatreonWebhookLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Content") - .HasColumnType("jsonb"); - - b.Property("Time") - .HasColumnType("timestamp with time zone"); - - b.Property("Trigger") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("PatreonWebhookLogs"); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.Patron", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CurrentTier") - .HasColumnType("text"); - - b.Property("PatreonId") - .IsRequired() - .HasColumnType("text"); - - b.Property("SpaceUserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("PatreonId") - .IsUnique(); - - b.HasIndex("SpaceUserId") - .IsUnique(); - - b.ToTable("Patrons"); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.SpaceRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("AspNetRoles", (string)null); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.SpaceUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("CreatedTime") - .HasColumnType("timestamp with time zone"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PasswordHash") - .HasColumnType("text"); - - b.Property("SecurityStamp") - .HasColumnType("text"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.ToTable("AspNetUsers", (string)null); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.UserOAuthClient", b => - { - b.Property("UserOAuthClientId") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("UserOAuthClientId")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("SpaceUserId") - .HasColumnType("uuid"); - - b.HasKey("UserOAuthClientId"); - - b.HasIndex("ClientId") - .IsUnique(); - - b.HasIndex("SpaceUserId"); - - b.ToTable("UserOAuthClients"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany("UserClaims") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany("Properties") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany("Scopes") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany("Secrets") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "Scope") - .WithMany("UserClaims") - .HasForeignKey("ScopeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Scope"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "Scope") - .WithMany("Properties") - .HasForeignKey("ScopeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Scope"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("Claims") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedCorsOrigins") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedGrantTypes") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("IdentityProviderRestrictions") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("PostLogoutRedirectUris") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("Properties") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("RedirectUris") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedScopes") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany("ClientSecrets") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") - .WithMany("UserClaims") - .HasForeignKey("IdentityResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityResource"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") - .WithMany("Properties") - .HasForeignKey("IdentityResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityResource"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("SS14.Auth.Shared.Data.SpaceRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("SS14.Auth.Shared.Data.SpaceUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("SS14.Auth.Shared.Data.SpaceUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("SS14.Auth.Shared.Data.SpaceRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SS14.Auth.Shared.Data.SpaceUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("SS14.Auth.Shared.Data.SpaceUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.AuthHash", b => - { - b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") - .WithMany("AuthHashes") - .HasForeignKey("SpaceUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("SpaceUser"); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.Discord", b => - { - b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") - .WithOne("Discord") - .HasForeignKey("SS14.Auth.Shared.Data.Discord", "SpaceUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("SpaceUser"); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.LoginSession", b => - { - b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") - .WithMany("LoginSessions") - .HasForeignKey("SpaceUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("SpaceUser"); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.Patron", b => - { - b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") - .WithOne("Patron") - .HasForeignKey("SS14.Auth.Shared.Data.Patron", "SpaceUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("SpaceUser"); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.UserOAuthClient", b => - { - b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") - .WithMany() - .HasForeignKey("SpaceUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - - b.Navigation("SpaceUser"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b => - { - b.Navigation("Properties"); - - b.Navigation("Scopes"); - - b.Navigation("Secrets"); - - b.Navigation("UserClaims"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => - { - b.Navigation("Properties"); - - b.Navigation("UserClaims"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b => - { - b.Navigation("AllowedCorsOrigins"); - - b.Navigation("AllowedGrantTypes"); - - b.Navigation("AllowedScopes"); - - b.Navigation("Claims"); - - b.Navigation("ClientSecrets"); - - b.Navigation("IdentityProviderRestrictions"); - - b.Navigation("PostLogoutRedirectUris"); - - b.Navigation("Properties"); - - b.Navigation("RedirectUris"); - }); - - modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b => - { - b.Navigation("Properties"); - - b.Navigation("UserClaims"); - }); - - modelBuilder.Entity("SS14.Auth.Shared.Data.SpaceUser", b => - { - b.Navigation("AuthHashes"); - - b.Navigation("Discord"); - - b.Navigation("LoginSessions"); - - b.Navigation("Patron"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.cs b/SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.cs deleted file mode 100644 index 45e5642..0000000 --- a/SS14.Auth.Shared/Data/Migrations/20220928002800_Discord.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace SS14.Auth.Shared.Data.Migrations -{ - public partial class Discord : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Discords", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - DiscordId = table.Column(type: "text", nullable: false), - SpaceUserId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Discords", x => x.Id); - table.ForeignKey( - name: "FK_Discords_AspNetUsers_SpaceUserId", - column: x => x.SpaceUserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_Discords_DiscordId", - table: "Discords", - column: "DiscordId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_Discords_SpaceUserId", - table: "Discords", - column: "SpaceUserId", - unique: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Discords"); - } - } -} diff --git a/SS14.Auth.Shared/Data/Migrations/20230218171536_DiscordLoginSession.Designer.cs b/SS14.Auth.Shared/Data/Migrations/20230218205029_DiscordLinking.Designer.cs similarity index 97% rename from SS14.Auth.Shared/Data/Migrations/20230218171536_DiscordLoginSession.Designer.cs rename to SS14.Auth.Shared/Data/Migrations/20230218205029_DiscordLinking.Designer.cs index 4a8a545..6074e6f 100644 --- a/SS14.Auth.Shared/Data/Migrations/20230218171536_DiscordLoginSession.Designer.cs +++ b/SS14.Auth.Shared/Data/Migrations/20230218205029_DiscordLinking.Designer.cs @@ -13,8 +13,8 @@ namespace SS14.Auth.Shared.Data.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20230218171536_DiscordLoginSession")] - partial class DiscordLoginSession + [Migration("20230218205029_DiscordLinking")] + partial class DiscordLinking { protected override void BuildTargetModel(ModelBuilder modelBuilder) { @@ -1072,32 +1072,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("BurnerEmails"); }); - modelBuilder.Entity("SS14.Auth.Shared.Data.Discord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DiscordId") - .IsRequired() - .HasColumnType("text"); - - b.Property("SpaceUserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("DiscordId") - .IsUnique(); - - b.HasIndex("SpaceUserId") - .IsUnique(); - - b.ToTable("Discords"); - }); - modelBuilder.Entity("SS14.Auth.Shared.Data.DiscordLoginSession", b => { b.Property("Id") @@ -1261,6 +1235,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); + b.Property("DiscordId") + .HasColumnType("text"); + b.Property("Email") .HasMaxLength(256) .HasColumnType("character varying(256)"); @@ -1585,17 +1562,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("SpaceUser"); }); - modelBuilder.Entity("SS14.Auth.Shared.Data.Discord", b => - { - b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") - .WithOne("Discord") - .HasForeignKey("SS14.Auth.Shared.Data.Discord", "SpaceUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("SpaceUser"); - }); - modelBuilder.Entity("SS14.Auth.Shared.Data.DiscordLoginSession", b => { b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") @@ -1711,8 +1677,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("AuthHashes"); - b.Navigation("Discord"); - b.Navigation("LoginSessions"); b.Navigation("PastAccountNames"); diff --git a/SS14.Auth.Shared/Data/Migrations/20230218171536_DiscordLoginSession.cs b/SS14.Auth.Shared/Data/Migrations/20230218205029_DiscordLinking.cs similarity index 81% rename from SS14.Auth.Shared/Data/Migrations/20230218171536_DiscordLoginSession.cs rename to SS14.Auth.Shared/Data/Migrations/20230218205029_DiscordLinking.cs index 86aef31..3b519ce 100644 --- a/SS14.Auth.Shared/Data/Migrations/20230218171536_DiscordLoginSession.cs +++ b/SS14.Auth.Shared/Data/Migrations/20230218205029_DiscordLinking.cs @@ -5,10 +5,16 @@ namespace SS14.Auth.Shared.Data.Migrations { - public partial class DiscordLoginSession : Migration + public partial class DiscordLinking : Migration { protected override void Up(MigrationBuilder migrationBuilder) { + migrationBuilder.AddColumn( + name: "DiscordId", + table: "AspNetUsers", + type: "text", + nullable: true); + migrationBuilder.CreateTable( name: "DiscordLoginSessions", columns: table => new @@ -38,6 +44,10 @@ protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "DiscordLoginSessions"); + + migrationBuilder.DropColumn( + name: "DiscordId", + table: "AspNetUsers"); } } } diff --git a/SS14.Auth.Shared/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/SS14.Auth.Shared/Data/Migrations/ApplicationDbContextModelSnapshot.cs index 48c51a9..9e63f09 100644 --- a/SS14.Auth.Shared/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/SS14.Auth.Shared/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1070,32 +1070,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("BurnerEmails"); }); - modelBuilder.Entity("SS14.Auth.Shared.Data.Discord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DiscordId") - .IsRequired() - .HasColumnType("text"); - - b.Property("SpaceUserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("DiscordId") - .IsUnique(); - - b.HasIndex("SpaceUserId") - .IsUnique(); - - b.ToTable("Discords"); - }); - modelBuilder.Entity("SS14.Auth.Shared.Data.DiscordLoginSession", b => { b.Property("Id") @@ -1259,6 +1233,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); + b.Property("DiscordId") + .HasColumnType("text"); + b.Property("Email") .HasMaxLength(256) .HasColumnType("character varying(256)"); @@ -1583,17 +1560,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("SpaceUser"); }); - modelBuilder.Entity("SS14.Auth.Shared.Data.Discord", b => - { - b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") - .WithOne("Discord") - .HasForeignKey("SS14.Auth.Shared.Data.Discord", "SpaceUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("SpaceUser"); - }); - modelBuilder.Entity("SS14.Auth.Shared.Data.DiscordLoginSession", b => { b.HasOne("SS14.Auth.Shared.Data.SpaceUser", "SpaceUser") @@ -1709,8 +1675,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("AuthHashes"); - b.Navigation("Discord"); - b.Navigation("LoginSessions"); b.Navigation("PastAccountNames"); diff --git a/SS14.Auth.Shared/Data/SpaceUser.cs b/SS14.Auth.Shared/Data/SpaceUser.cs index fdaeef1..5692da4 100644 --- a/SS14.Auth.Shared/Data/SpaceUser.cs +++ b/SS14.Auth.Shared/Data/SpaceUser.cs @@ -14,7 +14,7 @@ public class SpaceUser : IdentityUser public List AuthHashes { get; set; } = new List(); public Patron Patron { get; set; } - public Discord Discord { get; set; } + public string DiscordId { get; set; } public List PastAccountNames { get; set; } = default!; public List AccountLogs { get; set; } = default!; diff --git a/SS14.Auth/Controllers/DiscordLinkController.cs b/SS14.Auth/Controllers/DiscordLinkController.cs index 5206c79..213fc86 100644 --- a/SS14.Auth/Controllers/DiscordLinkController.cs +++ b/SS14.Auth/Controllers/DiscordLinkController.cs @@ -39,7 +39,7 @@ public async Task Callback(Guid state, string code) if (user == null) return NotFound("Session not exist or expired"); - if (user.Discord != null) + if (user.DiscordId != null) return BadRequest("⚠️ You already linked Discord with you account.\nAccount can be unlinked in account settings."); await _loginSessionManager.LinkDiscord(user, code); diff --git a/SS14.Auth/Controllers/QueryApiController.cs b/SS14.Auth/Controllers/QueryApiController.cs index a757d55..50a667f 100644 --- a/SS14.Auth/Controllers/QueryApiController.cs +++ b/SS14.Auth/Controllers/QueryApiController.cs @@ -52,9 +52,8 @@ internal static async Task BuildUserResponse( SpaceUser user) { var patronTier = await patreonDataManager.GetPatreonTierAsync(user); - var discordId = await discordDataManager.GetUserDiscordId(user); - return new QueryUserResponse(user.UserName!, user.Id, patronTier, discordId, user.CreatedTime); + return new QueryUserResponse(user.UserName!, user.Id, patronTier, user.DiscordId, user.CreatedTime); } private async Task DoResponse(SpaceUser? user) diff --git a/SS14.Web/Areas/Identity/Pages/Account/Manage/ManageDiscord.cshtml.cs b/SS14.Web/Areas/Identity/Pages/Account/Manage/ManageDiscord.cshtml.cs index 24f12cf..27ef094 100644 --- a/SS14.Web/Areas/Identity/Pages/Account/Manage/ManageDiscord.cshtml.cs +++ b/SS14.Web/Areas/Identity/Pages/Account/Manage/ManageDiscord.cshtml.cs @@ -3,10 +3,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using SS14.Auth.Shared.Config; using SS14.Auth.Shared.Data; namespace SS14.Web.Areas.Identity.Pages.Account.Manage @@ -16,7 +13,6 @@ public class ManageDiscord : PageModel private readonly UserManager _userManager; private readonly ILogger _logger; private readonly ApplicationDbContext _db; - private readonly IOptions _cfg; public bool DiscordLinked { get; private set; } @@ -38,9 +34,7 @@ public async Task OnGet() return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - var discord = await _db.Discords.SingleOrDefaultAsync(p => p.SpaceUserId == user.Id); - - DiscordLinked = discord != null; + DiscordLinked = user.DiscordId != null; return Page(); } @@ -53,13 +47,8 @@ public async Task OnPostUnlinkDiscordAsync() return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - var discord = await _db.Discords.SingleOrDefaultAsync(p => p.SpaceUserId == user.Id); - - if (discord != null) - { - _db.Discords.Remove(discord); - await _db.SaveChangesAsync(); - } + user.DiscordId = null; + await _db.SaveChangesAsync(); return RedirectToPage(); } @@ -84,4 +73,4 @@ public async Task OnPostLinkDiscordAsync() }, "Discord"); } } -} \ No newline at end of file +} diff --git a/SS14.Web/DiscordConnectionHandler.cs b/SS14.Web/DiscordConnectionHandler.cs index 365130e..2439c4a 100644 --- a/SS14.Web/DiscordConnectionHandler.cs +++ b/SS14.Web/DiscordConnectionHandler.cs @@ -32,26 +32,14 @@ public async Task HookReceivedTicket(TicketReceivedContext context) } var discordId = context.Principal!.Claims.First(p => p.Type == ClaimTypes.NameIdentifier).Value; - var existingDiscord = await _db.Discords.FirstOrDefaultAsync(a => a.DiscordId == discordId); - if (existingDiscord != null) - { - // Relinking - _db.Discords.Remove(existingDiscord); - } - existingDiscord = await _db.Discords.FirstOrDefaultAsync(a => a.SpaceUserId == user.Id); - if (existingDiscord != null) - { - // Relinking - _db.Discords.Remove(existingDiscord); - } + // Relinking + var currentOwner = await _db.Users.FirstOrDefaultAsync(a => a.DiscordId == discordId); + if (currentOwner != null) + currentOwner.DiscordId = null; + + user.DiscordId = discordId; - var discordLink = new Discord - { - DiscordId = discordId, - SpaceUserId = user.Id - }; - _db.Discords.Add(discordLink); await _db.SaveChangesAsync(); if (context.ReturnUri != null) @@ -62,4 +50,4 @@ public async Task HookReceivedTicket(TicketReceivedContext context) context.HandleResponse(); } } -} \ No newline at end of file +} From 876d94de6ffb4f4512cc355eb963db38bb8074d9 Mon Sep 17 00:00:00 2001 From: Morbo <14136326+Morb0@users.noreply.github.com> Date: Sun, 19 Feb 2023 00:05:14 +0300 Subject: [PATCH 9/9] Add example configs --- SS14.Auth/appsettings.yml | 8 +++++++- SS14.Web/appsettings.yml | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/SS14.Auth/appsettings.yml b/SS14.Auth/appsettings.yml index 6b5417a..b821f73 100644 --- a/SS14.Auth/appsettings.yml +++ b/SS14.Auth/appsettings.yml @@ -21,4 +21,10 @@ Serilog: AllowedHosts: "*" -WebBaseUrl: "https://localhost:5001/" \ No newline at end of file +WebBaseUrl: "https://localhost:5001/" + +Discord: + ClientId: "" + ClientSecret: "" + RedirectUri: "https://mysite.com/api/discord/callback" + diff --git a/SS14.Web/appsettings.yml b/SS14.Web/appsettings.yml index 90b6cef..b848f7c 100644 --- a/SS14.Web/appsettings.yml +++ b/SS14.Web/appsettings.yml @@ -22,4 +22,9 @@ Serilog: ForwardProxies: - 127.0.0.1 -AllowedHosts: "*" \ No newline at end of file +AllowedHosts: "*" + +Discord: + ClientId: "" + ClientSecret: "" + RedirectUri: "https://mysite.com/signin-discord"