1
0
mirror of https://git.teknik.io/Teknikode/Teknik.git synced 2023-08-02 14:16:22 +02:00

Migrated Identity Server 3 -> 4 and upgraded nuget/npm packages

This commit is contained in:
Uncled1023 2021-06-30 21:56:12 -07:00
parent a7a8c5499b
commit b17d916df4
30 changed files with 7727 additions and 936 deletions

View File

@ -83,32 +83,34 @@ namespace Teknik.IdentityServer.Configuration
};
}
public static IEnumerable<ApiScope> GetApiScopes()
{
return new List<ApiScope>()
{
new ApiScope(name: "teknik-api.read", displayName: "Teknik API Read Access"),
new ApiScope(name: "teknik-api.write", displayName: "Teknik API Write Access"),
new ApiScope(name: "auth-api") { Required = true, ShowInDiscoveryDocument = false }
};
}
public static IEnumerable<ApiResource> GetApiResources(Config config)
{
return new List<ApiResource> {
new ApiResource {
return new List<ApiResource>()
{
new ApiResource
{
Name = config.UserConfig.IdentityServerConfig.APIName,
DisplayName = "Teknik API",
Description = "Teknik API Access for end users",
UserClaims = new List<string> {"role", "username"},
ApiSecrets = new List<Secret> {new Secret(config.UserConfig.IdentityServerConfig.APISecret.Sha256()) },
Scopes = new List<Scope> {
new Scope("teknik-api.read", "Teknik API Read Access"),
new Scope("teknik-api.write", "Teknik API Write Access")
}
Scopes = new List<string> { "teknik-api.read", "teknik-api.write" }
},
new ApiResource {
Name = "auth-api",
DisplayName = "Auth Server API",
Description = "Auth Server API Access for managing the Auth Server",
Scopes = new List<Scope> {
new Scope()
{
Name = "auth-api",
ShowInDiscoveryDocument = false,
Required = true
}
}
Scopes = new List<string> { "auth-api" }
}
};
}
@ -118,7 +120,7 @@ namespace Teknik.IdentityServer.Configuration
{
public static IEnumerable<Policy> Get()
{
return new List<Policy>
return new List<Policy>()
{
new Policy
{

View File

@ -83,7 +83,7 @@ namespace Teknik.IdentityServer.Controllers
// if the user cancels, send a result back into IdentityServer as if they
// denied the consent (even if this client does not require consent).
// this will send back an access denied OIDC error response to the client.
await _interaction.GrantConsentAsync(context, ConsentResponse.Denied);
await _interaction.GrantConsentAsync(context, new ConsentResponse() { Error = AuthorizationError.AccessDenied });
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
return Redirect(returnUrl);

View File

@ -31,7 +31,7 @@ namespace Teknik.IdentityServer.Controllers
[HttpGet]
[AllowAnonymous]
[ResponseCache(Duration = 31536000, Location = ResponseCacheLocation.Any)]
public IActionResult Favicon([FromServices] IHostingEnvironment env)
public IActionResult Favicon([FromServices] IWebHostEnvironment env)
{
string imageFile = FileHelper.MapPath(env, Constants.FAVICON_PATH);
FileStream fs = new FileStream(imageFile, FileMode.Open, FileAccess.Read);
@ -41,7 +41,7 @@ namespace Teknik.IdentityServer.Controllers
// Get the Robots.txt
[HttpGet]
[AllowAnonymous]
public IActionResult Robots([FromServices] IHostingEnvironment env)
public IActionResult Robots([FromServices] IWebHostEnvironment env)
{
//string file = FileHelper.MapPath(env, Constants.ROBOTS_PATH);
return File(Constants.ROBOTS_PATH, "text/plain");

View File

@ -15,10 +15,11 @@ using Teknik.Configuration;
using Teknik.IdentityServer.ViewModels;
using Teknik.Logging;
using Teknik.Utilities;
using Teknik.WebCommon;
namespace Teknik.IdentityServer.Controllers
{
public class ErrorController : DefaultController
public class ErrorController : DefaultController, IErrorController
{
private readonly IIdentityServerInteractionService _interaction;
@ -27,8 +28,18 @@ namespace Teknik.IdentityServer.Controllers
_interaction = interaction;
}
[AllowAnonymous]
public IActionResult HttpError(int statusCode)
{
return HttpError(statusCode, null);
}
[AllowAnonymous]
public IActionResult HttpError(int statusCode, Exception ex)
{
if (ex != null)
return Http500(ex);
switch (statusCode)
{
case 401:
@ -37,11 +48,14 @@ namespace Teknik.IdentityServer.Controllers
return Http403();
case 404:
return Http404();
case 500:
return Http500(ex);
default:
return HttpGeneral(statusCode);
}
}
[AllowAnonymous]
public IActionResult HttpGeneral(int statusCode)
{
ViewBag.Title = statusCode;

View File

@ -58,7 +58,7 @@ namespace Teknik.IdentityServer.Controllers
private async Task<GrantsViewModel> BuildViewModelAsync()
{
var grants = await _interaction.GetAllUserConsentsAsync();
var grants = await _interaction.GetAllUserGrantsAsync();
var list = new List<GrantViewModel>();
foreach(var grant in grants)

View File

@ -1,6 +1,7 @@
using IdentityServer4.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
using Teknik.Configuration;
using Teknik.IdentityServer.Security;

View File

@ -15,29 +15,33 @@ namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.4-rtm-31024")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("ProductVersion", "2.1.4-rtm-31024")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
.ValueGeneratedOnAdd()
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex")
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles");
@ -47,14 +51,18 @@ namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("RoleId")
.IsRequired();
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
@ -67,14 +75,18 @@ namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired();
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
@ -85,14 +97,18 @@ namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderDisplayName");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired();
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("LoginProvider", "ProviderKey");
@ -103,9 +119,11 @@ namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("RoleId");
b.Property<string>("RoleId")
.HasColumnType("nvarchar(450)");
b.HasKey("UserId", "RoleId");
@ -116,13 +134,17 @@ namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("LoginProvider");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<string>("Value");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
@ -132,61 +154,82 @@ namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
modelBuilder.Entity("Teknik.IdentityServer.Models.ApplicationUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
.ValueGeneratedOnAdd()
.HasColumnType("nvarchar(450)");
b.Property<int>("AccessFailedCount");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<int>("AccountStatus");
b.Property<int>("AccountStatus")
.HasColumnType("int");
b.Property<int>("AccountType");
b.Property<int>("AccountType")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreationDate");
b.Property<DateTime>("CreationDate")
.HasColumnType("datetime2");
b.Property<string>("Email")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<DateTime>("LastEdit");
b.Property<DateTime>("LastEdit")
.HasColumnType("datetime2");
b.Property<DateTime>("LastSeen");
b.Property<DateTime>("LastSeen")
.HasColumnType("datetime2");
b.Property<bool>("LockoutEnabled");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PGPPublicKey");
b.Property<string>("PGPPublicKey")
.HasColumnType("nvarchar(max)");
b.Property<string>("PasswordHash");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex")
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers");
@ -194,7 +237,7 @@ namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
@ -202,7 +245,7 @@ namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
@ -210,7 +253,7 @@ namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
@ -218,12 +261,12 @@ namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
@ -231,7 +274,7 @@ namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);

View File

@ -0,0 +1,985 @@
// <auto-generated />
using System;
using IdentityServer4.EntityFramework.DbContexts;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.ConfigurationDb
{
[DbContext(typeof(ConfigurationDbContext))]
[Migration("20210630052232_V3toV4ConfigurationDb")]
partial class V3toV4ConfigurationDb
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("ProductVersion", "5.0.7")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("AllowedAccessTokenSigningAlgorithms")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("DisplayName")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<bool>("Enabled")
.HasColumnType("bit");
b.Property<DateTime?>("LastAccessed")
.HasColumnType("datetime2");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<bool>("NonEditable")
.HasColumnType("bit");
b.Property<bool>("ShowInDiscoveryDocument")
.HasColumnType("bit");
b.Property<DateTime?>("Updated")
.HasColumnType("datetime2");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("ApiResources");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ApiResourceId")
.HasColumnType("int");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("Id");
b.HasIndex("ApiResourceId");
b.ToTable("ApiResourceClaims");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ApiResourceId")
.HasColumnType("int");
b.Property<string>("Key")
.IsRequired()
.HasMaxLength(250)
.HasColumnType("nvarchar(250)");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.HasKey("Id");
b.HasIndex("ApiResourceId");
b.ToTable("ApiResourceProperties");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceScope", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ApiResourceId")
.HasColumnType("int");
b.Property<string>("Scope")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("Id");
b.HasIndex("ApiResourceId");
b.ToTable("ApiResourceScopes");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceSecret", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ApiResourceId")
.HasColumnType("int");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<DateTime?>("Expiration")
.HasColumnType("datetime2");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(250)
.HasColumnType("nvarchar(250)");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.HasKey("Id");
b.HasIndex("ApiResourceId");
b.ToTable("ApiResourceSecrets");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("DisplayName")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<bool>("Emphasize")
.HasColumnType("bit");
b.Property<bool>("Enabled")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<bool>("Required")
.HasColumnType("bit");
b.Property<bool>("ShowInDiscoveryDocument")
.HasColumnType("bit");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("ApiScopes");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ScopeId")
.HasColumnType("int");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("Id");
b.HasIndex("ScopeId");
b.ToTable("ApiScopeClaims");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeProperty", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("Key")
.IsRequired()
.HasMaxLength(250)
.HasColumnType("nvarchar(250)");
b.Property<int>("ScopeId")
.HasColumnType("int");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.HasKey("Id");
b.HasIndex("ScopeId");
b.ToTable("ApiScopeProperties");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("AbsoluteRefreshTokenLifetime")
.HasColumnType("int");
b.Property<int>("AccessTokenLifetime")
.HasColumnType("int");
b.Property<int>("AccessTokenType")
.HasColumnType("int");
b.Property<bool>("AllowAccessTokensViaBrowser")
.HasColumnType("bit");
b.Property<bool>("AllowOfflineAccess")
.HasColumnType("bit");
b.Property<bool>("AllowPlainTextPkce")
.HasColumnType("bit");
b.Property<bool>("AllowRememberConsent")
.HasColumnType("bit");
b.Property<string>("AllowedIdentityTokenSigningAlgorithms")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<bool>("AlwaysIncludeUserClaimsInIdToken")
.HasColumnType("bit");
b.Property<bool>("AlwaysSendClientClaims")
.HasColumnType("bit");
b.Property<int>("AuthorizationCodeLifetime")
.HasColumnType("int");
b.Property<bool>("BackChannelLogoutSessionRequired")
.HasColumnType("bit");
b.Property<string>("BackChannelLogoutUri")
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.Property<string>("ClientClaimsPrefix")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("ClientName")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("ClientUri")
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.Property<int?>("ConsentLifetime")
.HasColumnType("int");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<int>("DeviceCodeLifetime")
.HasColumnType("int");
b.Property<bool>("EnableLocalLogin")
.HasColumnType("bit");
b.Property<bool>("Enabled")
.HasColumnType("bit");
b.Property<bool>("FrontChannelLogoutSessionRequired")
.HasColumnType("bit");
b.Property<string>("FrontChannelLogoutUri")
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.Property<int>("IdentityTokenLifetime")
.HasColumnType("int");
b.Property<bool>("IncludeJwtId")
.HasColumnType("bit");
b.Property<DateTime?>("LastAccessed")
.HasColumnType("datetime2");
b.Property<string>("LogoUri")
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.Property<bool>("NonEditable")
.HasColumnType("bit");
b.Property<string>("PairWiseSubjectSalt")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("ProtocolType")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<int>("RefreshTokenExpiration")
.HasColumnType("int");
b.Property<int>("RefreshTokenUsage")
.HasColumnType("int");
b.Property<bool>("RequireClientSecret")
.HasColumnType("bit");
b.Property<bool>("RequireConsent")
.HasColumnType("bit");
b.Property<bool>("RequirePkce")
.HasColumnType("bit");
b.Property<bool>("RequireRequestObject")
.HasColumnType("bit");
b.Property<int>("SlidingRefreshTokenLifetime")
.HasColumnType("int");
b.Property<bool>("UpdateAccessTokenClaimsOnRefresh")
.HasColumnType("bit");
b.Property<DateTime?>("Updated")
.HasColumnType("datetime2");
b.Property<string>("UserCodeType")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<int?>("UserSsoLifetime")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ClientId")
.IsUnique();
b.ToTable("Clients");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ClientId")
.HasColumnType("int");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(250)
.HasColumnType("nvarchar(250)");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(250)
.HasColumnType("nvarchar(250)");
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientClaims");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ClientId")
.HasColumnType("int");
b.Property<string>("Origin")
.IsRequired()
.HasMaxLength(150)
.HasColumnType("nvarchar(150)");
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientCorsOrigins");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ClientId")
.HasColumnType("int");
b.Property<string>("GrantType")
.IsRequired()
.HasMaxLength(250)
.HasColumnType("nvarchar(250)");
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientGrantTypes");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ClientId")
.HasColumnType("int");
b.Property<string>("Provider")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientIdPRestrictions");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ClientId")
.HasColumnType("int");
b.Property<string>("PostLogoutRedirectUri")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientPostLogoutRedirectUris");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ClientId")
.HasColumnType("int");
b.Property<string>("Key")
.IsRequired()
.HasMaxLength(250)
.HasColumnType("nvarchar(250)");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientProperties");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ClientId")
.HasColumnType("int");
b.Property<string>("RedirectUri")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientRedirectUris");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ClientId")
.HasColumnType("int");
b.Property<string>("Scope")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientScopes");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ClientId")
.HasColumnType("int");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Description")
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.Property<DateTime?>("Expiration")
.HasColumnType("datetime2");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(250)
.HasColumnType("nvarchar(250)");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(4000)
.HasColumnType("nvarchar(4000)");
b.HasKey("Id");
b.HasIndex("ClientId");
b.ToTable("ClientSecrets");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("DisplayName")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<bool>("Emphasize")
.HasColumnType("bit");
b.Property<bool>("Enabled")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<bool>("NonEditable")
.HasColumnType("bit");
b.Property<bool>("Required")
.HasColumnType("bit");
b.Property<bool>("ShowInDiscoveryDocument")
.HasColumnType("bit");
b.Property<DateTime?>("Updated")
.HasColumnType("datetime2");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("IdentityResources");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceClaim", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("IdentityResourceId")
.HasColumnType("int");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("Id");
b.HasIndex("IdentityResourceId");
b.ToTable("IdentityResourceClaims");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("IdentityResourceId")
.HasColumnType("int");
b.Property<string>("Key")
.IsRequired()
.HasMaxLength(250)
.HasColumnType("nvarchar(250)");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.HasKey("Id");
b.HasIndex("IdentityResourceId");
b.ToTable("IdentityResourceProperties");
});
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("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");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.IO;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.ConfigurationDb
{
public partial class V3toV4ConfigurationDb : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
// Migrate config db to v4
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "Teknik.IdentityServer.Data.Migrations.IdentityServer.ConfigurationDb.ConfigurationDbContext.sql";
using Stream stream = assembly.GetManifestResourceStream(resourceName);
using StreamReader sr = new StreamReader(stream);
{
var sql = sr.ReadToEnd();
migrationBuilder.Sql(sql);
}
}
}
}

View File

@ -0,0 +1,303 @@
BEGIN TRANSACTION
--Add New Tables
-- Add ApiResourceScopes
CREATE TABLE ApiResourceScopes (
Id INT IDENTITY (1, 1) NOT NULL,
Scope NVARCHAR (200) NOT NULL,
ApiResourceId INT NOT NULL,
CONSTRAINT PK_ApiResourceScopes PRIMARY KEY CLUSTERED (Id ASC),
CONSTRAINT FK_ApiResourceScopes_ApiResources_ApiResourceId FOREIGN KEY (ApiResourceId) REFERENCES ApiResources (Id) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX IX_ApiResourceScopes_ApiResourceId
ON ApiResourceScopes(ApiResourceId ASC);
GO
-- Add ApiScopeProperties
CREATE TABLE ApiScopeProperties (
Id INT IDENTITY (1, 1) NOT NULL,
[Key] NVARCHAR (250) NOT NULL,
[Value] NVARCHAR (2000) NOT NULL,
ScopeId INT NOT NULL,
CONSTRAINT PK_ApiScopeProperties PRIMARY KEY CLUSTERED (Id ASC),
CONSTRAINT FK_ApiScopeProperties_ApiScopes_ScopeId FOREIGN KEY (ScopeId) REFERENCES ApiScopes (Id) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX IX_ApiScopeProperties_ScopeId
ON ApiScopeProperties(ScopeId ASC);
GO
-- Add Renamed Tables
-- ApiResourceClaims
CREATE TABLE [dbo].[ApiResourceClaims] (
Id INT IDENTITY (1, 1) NOT NULL,
[Type] NVARCHAR (200) NOT NULL,
ApiResourceId INT NOT NULL,
CONSTRAINT PK_ApiResourceClaims PRIMARY KEY CLUSTERED (Id ASC),
CONSTRAINT FK_ApiResourceClaims_ApiResources_ApiResourceId FOREIGN KEY (ApiResourceId) REFERENCES ApiResources (Id) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_ApiResourceClaims_ApiResourceId]
ON [dbo].[ApiResourceClaims]([ApiResourceId] ASC);
GO
-- ApiResourceProperties
CREATE TABLE [dbo].[ApiResourceProperties] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Key] NVARCHAR (250) NOT NULL,
[Value] NVARCHAR (2000) NOT NULL,
ApiResourceId INT NOT NULL,
CONSTRAINT PK_ApiResourceProperties PRIMARY KEY CLUSTERED (Id ASC),
CONSTRAINT FK_ApiResourceProperties_ApiResources_ApiResourceId FOREIGN KEY (ApiResourceId) REFERENCES ApiResources (Id) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_ApiResourceProperties_ApiResourceId]
ON [dbo].[ApiResourceProperties]([ApiResourceId] ASC);
GO
-- Add ApiResourceSecrets
CREATE TABLE ApiResourceSecrets (
Id INT IDENTITY (1, 1) NOT NULL,
[Description] NVARCHAR (1000) NULL,
[Value] NVARCHAR (4000) NOT NULL,
Expiration DATETIME2 (7) NULL,
[Type] NVARCHAR (250) NOT NULL,
Created DATETIME2 (7) NOT NULL,
ApiResourceId INT NOT NULL,
CONSTRAINT PK_ApiResourceSecrets PRIMARY KEY CLUSTERED (Id ASC),
CONSTRAINT FK_ApiResourceSecrets_ApiResources_ApiResourceId FOREIGN KEY (ApiResourceId) REFERENCES ApiResources (Id) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX IX_ApiResourceSecrets_ApiResourceId
ON ApiResourceSecrets(ApiResourceId ASC);
GO
-- IdentityResourceClaims
CREATE TABLE IdentityResourceClaims (
Id INT IDENTITY (1, 1) NOT NULL,
[Type] NVARCHAR (200) NOT NULL,
IdentityResourceId INT NOT NULL,
CONSTRAINT PK_IdentityResourceClaims PRIMARY KEY CLUSTERED (Id ASC),
CONSTRAINT FK_IdentityResourceClaims_IdentityResources_IdentityResourceId FOREIGN KEY (IdentityResourceId) REFERENCES IdentityResources (Id) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_IdentityResourceClaims_IdentityResourceId]
ON IdentityResourceClaims(IdentityResourceId ASC);
GO
-- IdentityResourceProperties
CREATE TABLE IdentityResourceProperties (
Id INT IDENTITY (1, 1) NOT NULL,
[Key] NVARCHAR (250) NOT NULL,
[Value] NVARCHAR (2000) NOT NULL,
IdentityResourceId INT NOT NULL,
CONSTRAINT PK_IdentityResourceProperties PRIMARY KEY CLUSTERED (Id ASC),
CONSTRAINT FK_IdentityResourceProperties_IdentityResources_IdentityResourceId FOREIGN KEY (IdentityResourceId) REFERENCES IdentityResources (Id) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX IX_IdentityResourceProperties_IdentityResourceId
ON IdentityResourceProperties(IdentityResourceId ASC);
GO
-- Migrate Existing Data
--ApiClaims -> ApiResourceClaims
SET IDENTITY_INSERT ApiResourceClaims ON;
INSERT INTO ApiResourceClaims
(Id, [Type], ApiResourceId)
SELECT
Id, [Type], ApiResourceId
FROM ApiClaims
SET IDENTITY_INSERT ApiResourceClaims OFF;
--ApiProperties -> ApiResourceProperties
SET IDENTITY_INSERT ApiResourceProperties ON;
GO
INSERT INTO ApiResourceProperties
(Id, [Key], [Value], ApiResourceId)
SELECT
Id, [Key], [Value], ApiResourceId
FROM ApiProperties
GO
SET IDENTITY_INSERT ApiResourceProperties OFF;
GO
--ApiSecrets -> ApiResourceSecrets
SET IDENTITY_INSERT ApiResourceSecrets ON;
GO
INSERT INTO ApiResourceSecrets
(Id, [Description], [Value], Expiration, [Type], Created, ApiResourceId)
SELECT
Id, [Description], [Value], Expiration, [Type], Created, ApiResourceId
FROM ApiSecrets
GO
SET IDENTITY_INSERT ApiResourceSecrets OFF;
GO
--IdentityClaims -> IdentityResourceClaims
SET IDENTITY_INSERT IdentityResourceClaims ON;
GO
INSERT INTO IdentityResourceClaims
(Id, [Type], IdentityResourceId)
SELECT
Id, [Type], IdentityResourceId
FROM IdentityClaims
GO
SET IDENTITY_INSERT IdentityResourceClaims OFF;
GO
--IdentityProperties -> IdentityResourceProperties
SET IDENTITY_INSERT IdentityResourceProperties ON;
GO
INSERT INTO IdentityResourceProperties
(Id, [Key], [Value], IdentityResourceId)
SELECT
Id, [Key], [Value], IdentityResourceId
FROM IdentityProperties
GO
SET IDENTITY_INSERT IdentityResourceProperties OFF;
GO
-- ApiScopes -> ApiResourceScopes
INSERT INTO ApiResourceScopes
([Scope], [ApiResourceId])
SELECT
[Name], [ApiResourceId]
FROM ApiScopes
-- Alter Existing Tables
-- ApiResources
ALTER TABLE ApiResources
ADD AllowedAccessTokenSigningAlgorithms NVARCHAR (100)
NULL
ALTER TABLE ApiResources
ADD ShowInDiscoveryDocument BIT
NULL
GO
UPDATE ApiResources SET ShowInDiscoveryDocument = 0
ALTER TABLE ApiResources
ALTER COLUMN ShowInDiscoveryDocument BIT NOT NULL
-- ApiScopeClaims
ALTER TABLE ApiScopeClaims
DROP CONSTRAINT FK_ApiScopeClaims_ApiScopes_ApiScopeId
DROP INDEX IX_ApiScopeClaims_ApiScopeId
ON ApiScopeClaims
exec sp_rename 'ApiScopeClaims.ApiScopeId', 'ScopeId', 'COLUMN';
CREATE NONCLUSTERED INDEX IX_ApiScopeClaims_ScopeId
ON ApiScopeClaims(ScopeId ASC);
ALTER TABLE ApiScopeClaims
ADD CONSTRAINT FK_ApiScopeClaims_ApiScopes_ScopeId
FOREIGN KEY (ScopeId) REFERENCES ApiScopes (Id) ON DELETE CASCADE
-- ApiScopes
ALTER TABLE ApiScopes
DROP CONSTRAINT FK_ApiScopes_ApiResources_ApiResourceId
DROP INDEX IX_ApiScopes_ApiResourceId
ON ApiScopes
ALTER TABLE ApiScopes
ADD [Enabled] BIT NULL
GO
UPDATE ApiScopes SET [Enabled] = 1
ALTER TABLE ApiScopes
DROP COLUMN ApiResourceId;
ALTER TABLE ApiScopes
ALTER COLUMN Enabled BIT NOT NULL;
-- Clients
ALTER TABLE Clients
ADD AllowedIdentityTokenSigningAlgorithms NVARCHAR (100) NULL
ALTER TABLE Clients
ADD RequireRequestObject BIT NULL
GO
UPDATE Clients SET RequireRequestObject = 0
ALTER TABLE Clients
ALTER COLUMN RequireRequestObject BIT NOT NULL
-- Delete Old Tables
--DROP TABLE ApiClaims
--DROP TABLE ApiProperties
--DROP TABLE ApiSecrets
--DROP TABLE IdentityClaims
--DROP TABLE IdentityProperties
COMMIT TRANSACTION

View File

@ -0,0 +1,129 @@
// <auto-generated />
using System;
using IdentityServer4.EntityFramework.DbContexts;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb
{
[DbContext(typeof(PersistedGrantDbContext))]
[Migration("20210630052538_V3toV4PersistedGrantDb")]
partial class V3toV4PersistedGrantDb
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("ProductVersion", "5.0.7")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
{
b.Property<string>("UserCode")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000)
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("DeviceCode")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime?>("Expiration")
.IsRequired()
.HasColumnType("datetime2");
b.Property<string>("SessionId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SubjectId")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("UserCode");
b.HasIndex("DeviceCode")
.IsUnique();
b.HasIndex("Expiration");
b.ToTable("DeviceCodes");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
{
b.Property<string>("Key")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime?>("ConsumedTime")
.HasColumnType("datetime2");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000)
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime?>("Expiration")
.HasColumnType("datetime2");
b.Property<string>("SessionId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SubjectId")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.HasKey("Key");
b.HasIndex("Expiration");
b.HasIndex("SubjectId", "ClientId", "Type");
b.HasIndex("SubjectId", "SessionId", "Type");
b.ToTable("PersistedGrants");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.IO;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb
{
public partial class V3toV4PersistedGrantDb : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
// Migrate config db to v4
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "Teknik.IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb.PersistedGrantDbContext.sql";
using Stream stream = assembly.GetManifestResourceStream(resourceName);
using StreamReader sr = new StreamReader(stream);
{
var sql = sr.ReadToEnd();
migrationBuilder.Sql(sql);
}
}
}
}

View File

@ -0,0 +1,30 @@
BEGIN TRANSACTION
-- Alter Existing Tables
-- DeviceCodes
ALTER TABLE DeviceCodes
ADD SessionId NVARCHAR (100) NULL
ALTER TABLE DeviceCodes
ADD [Description] NVARCHAR (200) NULL
-- PersistedGrants
ALTER TABLE PersistedGrants
ADD SessionId NVARCHAR (100) NULL
ALTER TABLE PersistedGrants
ADD [Description] NVARCHAR (200) NULL
ALTER TABLE PersistedGrants
ADD ConsumedTime DATETIME2 (7) NULL
CREATE NONCLUSTERED INDEX IX_PersistedGrants_SubjectId_SessionId_Type
ON PersistedGrants(SubjectId ASC, SessionId ASC, Type ASC);
COMMIT TRANSACTION;

View File

@ -15,35 +15,49 @@ namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.6-servicing-10079")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("ProductVersion", "5.0.7")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
{
b.Property<string>("UserCode")
.ValueGeneratedOnAdd()
.HasMaxLength(200);
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200);
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime>("CreationTime");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000);
.HasMaxLength(50000)
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("DeviceCode")
.IsRequired()
.HasMaxLength(200);
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime?>("Expiration")
.IsRequired();
.IsRequired()
.HasColumnType("datetime2");
b.Property<string>("SessionId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SubjectId")
.HasMaxLength(200);
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("UserCode");
@ -58,30 +72,52 @@ namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
{
b.Property<string>("Key")
.HasMaxLength(200);
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200);
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime>("CreationTime");
b.Property<DateTime?>("ConsumedTime")
.HasColumnType("datetime2");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000);
.HasMaxLength(50000)
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("Expiration");
b.Property<string>("Description")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime?>("Expiration")
.HasColumnType("datetime2");
b.Property<string>("SessionId")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("SubjectId")
.HasMaxLength(200);
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(50);
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.HasKey("Key");
b.HasIndex("SubjectId", "ClientId", "Type", "Expiration");
b.HasIndex("Expiration");
b.HasIndex("SubjectId", "ClientId", "Type");
b.HasIndex("SubjectId", "SessionId", "Type");
b.ToTable("PersistedGrants");
});

View File

@ -8,6 +8,11 @@
<Configurations>Debug;Release;Test</Configurations>
</PropertyGroup>
<ItemGroup>
<None Remove="Data\Migrations\IdentityServer\ConfigurationDb\ConfigurationDbContext.sql" />
<None Remove="Data\Migrations\IdentityServer\PersistedGrantDb\PersistedGrantDbContext.sql" />
</ItemGroup>
<ItemGroup>
<Folder Include="Middleware\" />
<Folder Include="App_Data\" />
@ -21,6 +26,12 @@
<PackageReference Include="IdentityServer4.EntityFramework" Version="4.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="5.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.7">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="5.0.2" />
</ItemGroup>
@ -28,9 +39,15 @@
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Data\Migrations\IdentityServer\ConfigurationDb\ConfigurationDbContext.sql" />
<EmbeddedResource Include="Data\Migrations\IdentityServer\PersistedGrantDb\PersistedGrantDbContext.sql" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Configuration\Configuration.csproj" />
<ProjectReference Include="..\Logging\Logging.csproj" />
<ProjectReference Include="..\WebCommon\WebCommon.csproj" />
</ItemGroup>
<ItemGroup>

View File

@ -1,118 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Teknik.Configuration;
namespace Teknik.IdentityServer.Middleware
{
public class BlacklistMiddleware
{
private readonly RequestDelegate _next;
private readonly IMemoryCache _cache;
public BlacklistMiddleware(RequestDelegate next, IMemoryCache cache)
{
_next = next;
_cache = cache;
}
public async Task Invoke(HttpContext context, Config config)
{
// Beggining of Request
bool blocked = false;
string blockReason = string.Empty;
#region Detect Blacklisted IPs
if (!blocked)
{
string IPAddr = context.Request.HttpContext.Connection.RemoteIpAddress.ToString();
if (!string.IsNullOrEmpty(IPAddr))
{
StringDictionary badIPs = GetFileData(context, "BlockedIPs", config.IPBlacklistFile);
blocked |= (badIPs != null && badIPs.ContainsKey(IPAddr));
blockReason = $"This IP address ({IPAddr}) has been blacklisted. If you feel this is in error, please contact support@teknik.io for assistance.";
}
}
#endregion
#region Detect Blacklisted Referrers
if (!blocked)
{
string referrer = context.Request.Headers["Referer"].ToString();
if (!string.IsNullOrEmpty(referrer))
{
StringDictionary badReferrers = GetFileData(context, "BlockedReferrers", config.ReferrerBlacklistFile);
blocked |= (badReferrers != null && badReferrers.ContainsKey(referrer));
blockReason = $"This referrer ({referrer}) has been blacklisted. If you feel this is in error, please contact support@teknik.io for assistance.";
}
}
#endregion
if (blocked)
{
// Clear the response
context.Response.Clear();
string jsonResult = JsonConvert.SerializeObject(new { error = new { type = "Blacklist", message = blockReason } });
await context.Response.WriteAsync(jsonResult);
return;
}
await _next.Invoke(context);
// End of request
}
public StringDictionary GetFileData(HttpContext context, string key, string filePath)
{
StringDictionary data;
if (!_cache.TryGetValue(key, out data))
{
data = GetFileLines(filePath);
_cache.Set(key, data);
}
return data;
}
public StringDictionary GetFileLines(string configPath)
{
StringDictionary retval = new StringDictionary();
if (File.Exists(configPath))
{
using (StreamReader sr = new StreamReader(configPath))
{
String line;
while ((line = sr.ReadLine()) != null)
{
line = line.Trim();
if (line.Length != 0)
{
retval.Add(line, null);
}
}
}
}
return retval;
}
}
public static class BlacklistMiddlewareExtensions
{
public static IApplicationBuilder UseBlacklist(this IApplicationBuilder builder)
{
return builder.UseMiddleware<BlacklistMiddleware>();
}
}
}

View File

@ -1,72 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Teknik.Configuration;
using Teknik.Utilities;
namespace Teknik.IdentityServer.Middleware
{
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class CORSMiddleware
{
private readonly RequestDelegate _next;
public CORSMiddleware(RequestDelegate next)
{
_next = next;
}
public Task InvokeAsync(HttpContext httpContext, Config config)
{
// Allow this domain, or everything if local
string origin = (httpContext.Request.IsLocal()) ? "*" : httpContext.Request.Headers["Origin"].ToString();
// Is the referrer set to the CDN and we are using a CDN?
if (config.UseCdn && !string.IsNullOrEmpty(config.CdnHost))
{
try
{
string host = httpContext.Request.Headers["Host"];
Uri uri = new Uri(config.CdnHost);
if (host == uri.Host)
origin = host;
}
catch { }
}
string domain = (string.IsNullOrEmpty(origin)) ? string.Empty : origin.GetDomain();
if (string.IsNullOrEmpty(origin))
{
string host = httpContext.Request.Headers["Host"];
string sub = host.GetSubdomain();
origin = (string.IsNullOrEmpty(sub)) ? config.Host : sub + "." + config.Host;
}
else
{
if (domain != config.Host)
{
string sub = origin.GetSubdomain();
origin = (string.IsNullOrEmpty(sub)) ? config.Host : sub + "." + config.Host;
}
}
httpContext.Response.Headers.Append("Access-Control-Allow-Origin", origin);
httpContext.Response.Headers.Append("Vary", "Origin");
return _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class CORSMiddlewareExtensions
{
public static IApplicationBuilder UseCORS(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CORSMiddleware>();
}
}
}

View File

@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Teknik.Configuration;
using Teknik.Utilities;
using Teknik.Utilities.Routing;
namespace Teknik.IdentityServer.Middleware
{
@ -19,7 +20,7 @@ namespace Teknik.IdentityServer.Middleware
_next = next;
}
public Task Invoke(HttpContext httpContext, Config config)
public Task Invoke(HttpContext httpContext)
{
if (!httpContext.Request.IsLocal())
{

View File

@ -1,99 +0,0 @@
using IdentityServer4.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Teknik.Configuration;
using Teknik.IdentityServer.Controllers;
using Teknik.Logging;
using Teknik.Utilities;
namespace Teknik.IdentityServer.Middleware
{
public class ErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly IRouter _router;
public ErrorHandlerMiddleware(RequestDelegate next, IRouter router)
{
_next = next;
_router = router;
}
public async Task Invoke(HttpContext httpContext, ILogger<Logger> logger, Config config, IIdentityServerInteractionService interaction)
{
var statusCodeFeature = new StatusCodePagesFeature();
httpContext.Features.Set<IStatusCodePagesFeature>(statusCodeFeature);
Exception exception = null;
try
{
await _next(httpContext);
}
catch (Exception ex)
{
httpContext.Response.StatusCode = 500;
exception = ex;
}
if (!statusCodeFeature.Enabled)
{
// Check if the feature is still available because other middleware (such as a web API written in MVC) could
// have disabled the feature to prevent HTML status code responses from showing up to an API client.
return;
}
// Do nothing if a response body has already been provided or not 404 response
if (httpContext.Response.HasStarted)
{
return;
}
// Detect if there is a response code or exception occured
if ((httpContext.Response.StatusCode >= 400 && httpContext.Response.StatusCode <= 600) || exception != null)
{
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");
routeData.Routers.Add(_router);
var context = new ControllerContext();
context.HttpContext = httpContext;
context.RouteData = routeData;
context.ActionDescriptor = new Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor();
ErrorController errorController = new ErrorController(logger, config, interaction);
errorController.ControllerContext = context;
if (httpContext.Response.StatusCode == 500)
{
await errorController.Http500(exception).ExecuteResultAsync(context);
}
else
{
await errorController.HttpError(httpContext.Response.StatusCode).ExecuteResultAsync(context);
}
}
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class SetupErrorHandlerMiddlewareExtensions
{
public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder builder, Config config)
{
return builder.UseMiddleware<ErrorHandlerMiddleware>();
}
}
}

View File

@ -1,68 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Teknik.Configuration;
using Teknik.Utilities;
namespace Teknik.IdentityServer.Middleware
{
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class PerformanceMonitorMiddleware
{
private readonly RequestDelegate _next;
public PerformanceMonitorMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext, Config config)
{
Stopwatch timer = new Stopwatch();
timer.Start();
httpContext.Response.OnStarting(state =>
{
var context = (HttpContext)state;
timer.Stop();
double ms = (double)timer.ElapsedMilliseconds;
string result = string.Format("{0:F0}", ms);
if (!httpContext.Response.Headers.IsReadOnly)
httpContext.Response.Headers.Add("GenerationTime", result);
return Task.CompletedTask;
}, httpContext);
await _next(httpContext);
// Don't interfere with non-HTML responses
if (httpContext.Response.ContentType != null && httpContext.Response.ContentType.StartsWith("text/html") && httpContext.Response.StatusCode == 200 && !httpContext.Request.IsAjaxRequest())
{
double ms = (double)timer.ElapsedMilliseconds;
string result = string.Format("{0:F0}", ms);
await httpContext.Response.WriteAsync(
"<script nonce=\"" + httpContext.Items[Constants.NONCE_KEY] + "\">" +
"var pageGenerationTime = '" + result + "';" +
"pageloadStopTimer();" +
"</script >");
}
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class PerformanceMonitorMiddlewareExtensions
{
public static IApplicationBuilder UsePerformanceMonitor(this IApplicationBuilder builder)
{
return builder.UseMiddleware<PerformanceMonitorMiddleware>();
}
}
}

View File

@ -1,53 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Teknik.Configuration;
namespace Teknik.IdentityServer.Middleware
{
public class SecurityHeadersMiddleware
{
private readonly RequestDelegate _next;
public SecurityHeadersMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext httpContext, Config config)
{
IHeaderDictionary headers = httpContext.Response.Headers;
// Access Control
headers.Append("Access-Control-Allow-Credentials", "true");
headers.Append("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS");
headers.Append("Access-Control-Allow-Headers", "Authorization, Accept, Origin, Content-Type, X-Requested-With, Connection, Transfer-Encoding");
// HSTS
headers.Append("strict-transport-security", "max-age=31536000; includeSubdomains; preload");
// XSS Protection
headers.Append("X-XSS-Protection", "1; mode=block");
// Content Type Options
headers.Append("X-Content-Type-Options", "nosniff");
// Referrer Policy
headers.Append("Referrer-Policy", "no-referrer, strict-origin-when-cross-origin");
return _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class SecurityHeadersMiddlewareExtensions
{
public static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder builder)
{
return builder.UseMiddleware<SecurityHeadersMiddleware>();
}
}
}

View File

@ -1,40 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Teknik.Configuration;
using Teknik.Utilities;
namespace Teknik.IdentityServer.Middleware
{
public class SetupHttpContextMiddleware
{
private readonly RequestDelegate _next;
public SetupHttpContextMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
// Generate the NONCE used for this request
string nonce = Convert.ToBase64String(Encoding.UTF8.GetBytes(StringHelper.RandomString(24)));
httpContext.Items[Constants.NONCE_KEY] = nonce;
await _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class SetupHttpContextMiddlewareExtensions
{
public static IApplicationBuilder UseHttpContextSetup(this IApplicationBuilder builder)
{
return builder.UseMiddleware<SetupHttpContextMiddleware>();
}
}
}

View File

@ -1,7 +1,10 @@
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console;
using System;
using System.IO;
using Teknik.Configuration;
using Teknik.Logging;
@ -12,26 +15,31 @@ namespace Teknik.IdentityServer
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior",
true);
CreateHostBuilder(args).Build().Run();
}
public static IWebHost BuildWebHost(string[] args)
public static IHostBuilder CreateHostBuilder(string[] args)
{
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true)
.AddCommandLine(args)
.Build();
return WebHost.CreateDefaultBuilder(args)
.UseConfiguration(config)
.UseStartup<Startup>()
return Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(config =>
{
config.AddJsonFile("appsettings.json", optional: true);
config.AddCommandLine(args);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureLogging((hostingContext, logging) =>
{
string baseDir = hostingContext.HostingEnvironment.ContentRootPath;
string dataDir = Path.Combine(baseDir, "App_Data");
logging.AddProvider(new LoggerProvider(Config.Load(dataDir)));
})
.Build();
logging.AddFilter<ConsoleLoggerProvider>("Microsoft.AspNetCore.Routing", LogLevel.Trace);
});
}
}
}

View File

@ -36,9 +36,9 @@ namespace Teknik.IdentityServer.Services
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
var allowLocal = true;
if (context?.ClientId != null)
if (context?.Client?.ClientId != null)
{
var client = await _clientStore.FindEnabledClientByIdAsync(context.ClientId);
var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId);
if (client != null)
{
allowLocal = client.EnableLocalLogin;

View File

@ -1,7 +1,9 @@
using IdentityServer4.Models;
using IdentityServer4.Services;
using IdentityServer4.Stores;
using IdentityServer4.Validation;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Teknik.IdentityServer.Models;
@ -38,7 +40,7 @@ namespace Teknik.IdentityServer.Services
// user clicked 'no' - send back the standard 'access_denied' response
if (model.Button == "no")
{
grantedConsent = ConsentResponse.Denied;
grantedConsent = new ConsentResponse() { Error = AuthorizationError.AccessDenied };
}
// user clicked 'yes' - validate the data
else if (model.Button == "yes" && model != null)
@ -55,7 +57,7 @@ namespace Teknik.IdentityServer.Services
grantedConsent = new ConsentResponse
{
RememberConsent = model.RememberConsent,
ScopesConsented = scopes.ToArray()
ScopesValuesConsented = scopes.ToArray()
};
}
else
@ -94,22 +96,22 @@ namespace Teknik.IdentityServer.Services
var request = await _interaction.GetAuthorizationContextAsync(returnUrl);
if (request != null)
{
var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId);
var client = await _clientStore.FindEnabledClientByIdAsync(request.Client.ClientId);
if (client != null)
{
var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);
var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ValidatedResources.RawScopeValues);
if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any()))
{
return CreateConsentViewModel(model, returnUrl, request, client, resources);
}
else
{
_logger.LogError("No scopes matching: {0}", request.ScopesRequested.Aggregate((x, y) => x + ", " + y));
_logger.LogError("No scopes matching: {0}", request.ValidatedResources.RawScopeValues.Aggregate((x, y) => x + ", " + y));
}
}
else
{
_logger.LogError("Invalid client id: {0}", request.ClientId);
_logger.LogError("Invalid client id: {0}", request.Client.ClientId);
}
}
else
@ -137,13 +139,22 @@ namespace Teknik.IdentityServer.Services
vm.AllowRememberConsent = client.AllowRememberConsent;
vm.IdentityScopes = resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray();
vm.ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray();
if (ConsentOptions.EnableOfflineAccess && resources.OfflineAccess)
var apiScopes = new List<ScopeViewModel>();
foreach (var parsedScope in request.ValidatedResources.ParsedScopes)
{
vm.ResourceScopes = vm.ResourceScopes.Union(new ScopeViewModel[] {
GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)
});
var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName);
if (apiScope != null)
{
var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null);
apiScopes.Add(scopeVm);
}
}
if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess)
{
apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null));
}
vm.ResourceScopes = apiScopes;
return vm;
}
@ -161,16 +172,22 @@ namespace Teknik.IdentityServer.Services
};
}
public ScopeViewModel CreateScopeViewModel(Scope scope, bool check)
public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check)
{
var displayName = apiScope.DisplayName ?? apiScope.Name;
if (!string.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter))
{
displayName += ":" + parsedScopeValue.ParsedParameter;
}
return new ScopeViewModel
{
Name = scope.Name,
DisplayName = scope.DisplayName,
Description = scope.Description,
Emphasize = scope.Emphasize,
Required = scope.Required,
Checked = check || scope.Required
Name = parsedScopeValue.RawValue,
DisplayName = displayName,
Description = apiScope.Description,
Emphasize = apiScope.Emphasize,
Required = apiScope.Required,
Checked = check || apiScope.Required
};
}

View File

@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Claims;
using Microsoft.EntityFrameworkCore;
using IdentityServer4.EntityFramework.DbContexts;
using IdentityServer4.EntityFramework.Mappers;
using Microsoft.AspNetCore.Builder;
@ -10,7 +11,6 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@ -23,24 +23,28 @@ using Teknik.Logging;
using Microsoft.AspNetCore.Authorization;
using Teknik.IdentityServer.Models;
using IdentityServer4.Services;
using Teknik.WebCommon.Middleware;
using Microsoft.Extensions.Hosting;
using Teknik.Middleware;
using Teknik.WebCommon;
using Teknik.IdentityServer.Controllers;
namespace Teknik.IdentityServer
{
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment env)
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
Environment = env;
}
public IConfiguration Configuration { get; }
public IHostingEnvironment Environment { get; }
public IWebHostEnvironment Environment { get; }
public void ConfigureServices(IServiceCollection services)
{
string dataDir = Configuration["ConfigDirectory"];
string dataDir = (Configuration != null) ? Configuration["ConfigDirectory"] : null;
if (string.IsNullOrEmpty(dataDir))
{
string baseDir = Environment.ContentRootPath;
@ -58,14 +62,17 @@ namespace Teknik.IdentityServer
// Resolve the services from the service provider
var config = sp.GetService<Config>();
var devEnv = config?.DevEnvironment ?? true;
var defaultConn = config?.DbConnection ?? string.Empty;
var authority = config?.UserConfig?.IdentityServerConfig?.Authority ?? string.Empty;
if (config.DevEnvironment)
if (devEnv)
{
Environment.EnvironmentName = EnvironmentName.Development;
Environment.EnvironmentName = Environments.Development;
}
else
{
Environment.EnvironmentName = EnvironmentName.Production;
Environment.EnvironmentName = Environments.Production;
}
services.ConfigureApplicationCookie(options =>
@ -73,7 +80,6 @@ namespace Teknik.IdentityServer
options.Cookie.Name = "TeknikAuth";
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
options.Cookie.Expiration = TimeSpan.FromDays(30);
options.ExpireTimeSpan = TimeSpan.FromDays(30);
});
@ -87,6 +93,10 @@ namespace Teknik.IdentityServer
#endif
});
services.AddScoped<IErrorController, ErrorController>();
services.AddControllersWithViews()
.AddControllersAsServices();
// Sessions
services.AddResponseCaching();
services.AddMemoryCache();
@ -100,10 +110,12 @@ namespace Teknik.IdentityServer
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
//services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddDbContext<ApplicationDbContext>(builder =>
builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)));
services.AddDbContext<ApplicationDbContext>(options => options
.UseLazyLoadingProxies()
.UseSqlServer(defaultConn, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)),
ServiceLifetime.Transient);
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
@ -136,10 +148,10 @@ namespace Teknik.IdentityServer
})
.AddOperationalStore(options =>
options.ConfigureDbContext = builder =>
builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
builder.UseSqlServer(defaultConn, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
.AddConfigurationStore(options =>
options.ConfigureDbContext = builder =>
builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
builder.UseSqlServer(defaultConn, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
.AddConfigurationStoreCache()
.AddAspNetIdentity<ApplicationUser>()
.AddRedirectUriValidator<TeknikRedirectUriValidator>()
@ -162,7 +174,7 @@ namespace Teknik.IdentityServer
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = config.UserConfig.IdentityServerConfig.Authority;
options.Authority = authority;
options.RequireHttpsMetadata = true;
options.ApiName = "auth-api";
@ -172,8 +184,23 @@ namespace Teknik.IdentityServer
services.AddTransient<IProfileService, TeknikProfileService>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, Config config)
public void Configure(IApplicationBuilder app, ApplicationDbContext dbContext, Config config)
{
// Create and Migrate the database
dbContext?.Database?.Migrate();
// Setup static files and cache them client side
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers[HeaderNames.CacheControl] = "public,max-age=" + 31536000;
}
});
// Initiate Routing
app.UseRouting();
// Setup the HttpContext
app.UseHttpContextSetup();
@ -189,13 +216,11 @@ namespace Teknik.IdentityServer
}
});
// Use Exception Handling
app.UseErrorHandler(config);
// Force a HTTPS redirection (301)
app.UseHttpsRedirection();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// Use Exception Handling
app.UseErrorHandler();
// Custom Middleware
app.UseBlacklist();
@ -206,26 +231,18 @@ namespace Teknik.IdentityServer
// Cache Responses
app.UseResponseCaching();
// Force a HTTPS redirection (301)
app.UseHttpsRedirection();
// Setup static files anc cache them client side
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers[HeaderNames.CacheControl] = "public,max-age=" + 31536000;
}
});
InitializeDbTestDataAsync(app, config).Wait();
if (config != null)
InitializeDbTestDataAsync(app, config);
app.UseIdentityServer();
app.UseMvcWithDefaultRoute();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
private static async System.Threading.Tasks.Task InitializeDbTestDataAsync(IApplicationBuilder app, Config config)
private static void InitializeDbTestDataAsync(IApplicationBuilder app, Config config)
{
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
@ -253,6 +270,15 @@ namespace Teknik.IdentityServer
context.SaveChanges();
}
if (!context.ApiScopes.Any())
{
foreach (var apiScope in Resources.GetApiScopes())
{
context.ApiScopes.Add(apiScope.ToEntity());
}
context.SaveChanges();
}
if (!context.ApiResources.Any())
{
foreach (var resource in Resources.GetApiResources(config))

View File

@ -4,8 +4,7 @@
"inputFiles": [
"./wwwroot/lib/jquery/js/jquery.js",
"./wwwroot/lib/jquery/js/jquery.validate.js",
"./wwwroot/lib/bootstrap/js/bootstrap.js",
"./wwwroot/js/app/common.js"
"./wwwroot/lib/bootstrap/js/bootstrap.js"
]
},
{

File diff suppressed because it is too large Load Diff

View File

@ -4,22 +4,22 @@
"awesome-bootstrap-checkbox": "^1.0.1",
"bootstrap": "^3.4.1",
"font-awesome": "^4.7.0",
"jquery": "^3.4.1",
"jquery-validation": "^1.19.1"
"jquery": "^3.6.0",
"jquery-validation": "^1.19.3"
},
"description": "Teknik Services",
"devDependencies": {
"del": "^3.0.0",
"git-rev-sync": "^1.12.0",
"del": "^6.0.0",
"git-rev-sync": "^3.0.1",
"gulp": "^4.0.2",
"gulp-concat": "^2.6.1",
"gulp-cssmin": "^0.2.0",
"gulp-rename": "^1.3.0",
"gulp-replace": "^1.0.0",
"gulp-rename": "^2.0.0",
"gulp-replace": "^1.1.3",
"gulp-uglify": "^3.0.2",
"merge-stream": "^1.0.1",
"merge-stream": "^2.0.0",
"pump": "^3.0.0",
"rimraf": "^2.6.3",
"rimraf": "^3.0.2",
"uglify-es": "^3.3.10"
},
"keywords": [
@ -29,7 +29,7 @@
],
"license": "BSD-3-Clause",
"main": "gulpfile.js",
"name": "teknik",
"name": "teknik-identity",
"repository": {
"type": "git",
"url": "https://git.teknik.io/Teknikode/Teknik"