From fc86f237fb3f02a4dbfe40594164ab5d858c922a Mon Sep 17 00:00:00 2001 From: Uncled1023 Date: Mon, 28 Jan 2019 23:15:00 -0800 Subject: [PATCH] Added cacheing to Client Store, and Management API --- .../Controllers/ManageController.cs | 113 ++++++-- .../20190129061223_UserEditDate.Designer.cs | 244 ++++++++++++++++++ .../20190129061223_UserEditDate.cs | 24 ++ .../ApplicationDbContextModelSnapshot.cs | 4 +- IdentityServer/Models/ApplicationUser.cs | 5 + IdentityServer/Startup.cs | 5 +- .../Areas/Admin/Views/Admin/UserSearch.cshtml | 2 +- Teknik/bundleconfig.json | 2 +- Utilities/MemoryCacheHelper.cs | 33 +++ 9 files changed, 408 insertions(+), 24 deletions(-) create mode 100644 IdentityServer/Data/Migrations/ApplicationDb/20190129061223_UserEditDate.Designer.cs create mode 100644 IdentityServer/Data/Migrations/ApplicationDb/20190129061223_UserEditDate.cs create mode 100644 Utilities/MemoryCacheHelper.cs diff --git a/IdentityServer/Controllers/ManageController.cs b/IdentityServer/Controllers/ManageController.cs index 29a8b3a..70bda8a 100644 --- a/IdentityServer/Controllers/ManageController.cs +++ b/IdentityServer/Controllers/ManageController.cs @@ -14,10 +14,13 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; using Teknik.Configuration; using Teknik.IdentityServer.Models; using Teknik.IdentityServer.Models.Manage; +using Teknik.IdentityServer.Services; using Teknik.Logging; using Teknik.Utilities; @@ -28,17 +31,22 @@ namespace Teknik.IdentityServer.Controllers [ApiController] public class ManageController : DefaultController { + private const string _UserInfoCacheKey = "UserInfo"; + private readonly UserManager _userManager; private readonly SignInManager _signInManager; + private readonly IMemoryCache _cache; public ManageController( ILogger logger, Config config, UserManager userManager, - SignInManager signInManager) : base(logger, config) + SignInManager signInManager, + IMemoryCache cache) : base(logger, config) { _userManager = userManager; _signInManager = signInManager; + _cache = cache; } [HttpPost] @@ -91,7 +99,11 @@ namespace Teknik.IdentityServer.Controllers var result = await _userManager.DeleteAsync(foundUser); if (result.Succeeded) + { + RemoveCachedUser(model.Username); + return new JsonResult(new { success = true }); + } else return new JsonResult(new { success = false, message = "Unable to delete user.", identityErrors = result.Errors }); } @@ -104,8 +116,8 @@ namespace Teknik.IdentityServer.Controllers { if (string.IsNullOrEmpty(username)) return new JsonResult(new { success = false, message = "Username is required" }); - - var foundUser = await _userManager.FindByNameAsync(username); + + var foundUser = await GetCachedUser(username); return new JsonResult(new { success = true, data = foundUser != null }); } @@ -115,12 +127,11 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(username)) return new JsonResult(new { success = false, message = "Username is required" }); - var foundUser = await _userManager.FindByNameAsync(username); + var foundUser = await GetCachedUser(username); if (foundUser != null) { return new JsonResult(new { success = true, data = foundUser.ToJson() }); } - return new JsonResult(new { success = false, message = "User does not exist." }); } @@ -132,7 +143,7 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(model.Password)) return new JsonResult(new { success = false, message = "Password is required" }); - var foundUser = await _userManager.FindByNameAsync(model.Username); + var foundUser = await GetCachedUser(model.Username); if (foundUser != null) { bool valid = await _userManager.CheckPasswordAsync(foundUser, model.Password); @@ -148,7 +159,7 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(model.Username)) return new JsonResult(new { success = false, message = "Username is required" }); - var foundUser = await _userManager.FindByNameAsync(model.Username); + var foundUser = await GetCachedUser(model.Username); if (foundUser != null) { string token = await _userManager.GeneratePasswordResetTokenAsync(foundUser); @@ -168,7 +179,7 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(model.Password)) return new JsonResult(new { success = false, message = "Password is required" }); - var foundUser = await _userManager.FindByNameAsync(model.Username); + var foundUser = await GetCachedUser(model.Username); if (foundUser != null) { var result = await _userManager.ResetPasswordAsync(foundUser, model.Token, model.Password); @@ -191,7 +202,7 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(model.NewPassword)) return new JsonResult(new { success = false, message = "New Password is required" }); - var foundUser = await _userManager.FindByNameAsync(model.Username); + var foundUser = await GetCachedUser(model.Username); if (foundUser != null) { var result = await _userManager.ChangePasswordAsync(foundUser, model.CurrentPassword, model.NewPassword); @@ -210,12 +221,15 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(model.Username)) return new JsonResult(new { success = false, message = "Username is required" }); - var foundUser = await _userManager.FindByNameAsync(model.Username); + var foundUser = await GetCachedUser(model.Username); if (foundUser != null) { var result = await _userManager.SetEmailAsync(foundUser, model.Email); if (result.Succeeded) { + // Remove the UserInfo Cache + RemoveCachedUser(model.Username); + var token = await _userManager.GenerateEmailConfirmationTokenAsync(foundUser); return new JsonResult(new { success = true, data = token }); } @@ -234,9 +248,12 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(model.Token)) return new JsonResult(new { success = false, message = "Token is required" }); - var foundUser = await _userManager.FindByNameAsync(model.Username); + var foundUser = await GetCachedUser(model.Username); if (foundUser != null) { + // Remove the UserInfo Cache + RemoveCachedUser(model.Username); + var result = await _userManager.ConfirmEmailAsync(foundUser, model.Token); if (result.Succeeded) return new JsonResult(new { success = true }); @@ -253,14 +270,19 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(model.Username)) return new JsonResult(new { success = false, message = "Username is required" }); - var foundUser = await _userManager.FindByNameAsync(model.Username); + var foundUser = await GetCachedUser(model.Username); if (foundUser != null) { foundUser.AccountStatus = model.AccountStatus; var result = await _userManager.UpdateAsync(foundUser); if (result.Succeeded) + { + // Remove the UserInfo Cache + RemoveCachedUser(model.Username); + return new JsonResult(new { success = true }); + } else return new JsonResult(new { success = false, message = "Unable to update account status.", identityErrors = result.Errors }); } @@ -274,14 +296,19 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(model.Username)) return new JsonResult(new { success = false, message = "Username is required" }); - var foundUser = await _userManager.FindByNameAsync(model.Username); + var foundUser = await GetCachedUser(model.Username); if (foundUser != null) { foundUser.AccountType = model.AccountType; var result = await _userManager.UpdateAsync(foundUser); if (result.Succeeded) + { + // Remove the UserInfo Cache + RemoveCachedUser(model.Username); + return new JsonResult(new { success = true }); + } else return new JsonResult(new { success = false, message = "Unable to update account type.", identityErrors = result.Errors }); } @@ -295,14 +322,19 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(model.Username)) return new JsonResult(new { success = false, message = "Username is required" }); - var foundUser = await _userManager.FindByNameAsync(model.Username); + var foundUser = await GetCachedUser(model.Username); if (foundUser != null) { foundUser.PGPPublicKey = model.PGPPublicKey; var result = await _userManager.UpdateAsync(foundUser); if (result.Succeeded) + { + // Remove the UserInfo Cache + RemoveCachedUser(model.Username); + return new JsonResult(new { success = true }); + } else return new JsonResult(new { success = false, message = "Unable to update pgp public key.", identityErrors = result.Errors }); } @@ -316,7 +348,7 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(username)) return new JsonResult(new { success = false, message = "Username is required" }); - var foundUser = await _userManager.FindByNameAsync(username); + var foundUser = await GetCachedUser(username); if (foundUser != null) { string unformattedKey = await _userManager.GetAuthenticatorKeyAsync(foundUser); @@ -333,9 +365,12 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(model.Username)) return new JsonResult(new { success = false, message = "Username is required" }); - var foundUser = await _userManager.FindByNameAsync(model.Username); + var foundUser = await GetCachedUser(model.Username); if (foundUser != null) { + // Remove the UserInfo Cache + RemoveCachedUser(model.Username); + await _userManager.ResetAuthenticatorKeyAsync(foundUser); string unformattedKey = await _userManager.GetAuthenticatorKeyAsync(foundUser); @@ -353,7 +388,7 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(model.Code)) return new JsonResult(new { success = false, message = "Code is required" }); - var foundUser = await _userManager.FindByNameAsync(model.Username); + var foundUser = await GetCachedUser(model.Username); if (foundUser != null) { // Strip spaces and hypens @@ -367,6 +402,9 @@ namespace Teknik.IdentityServer.Controllers var result = await _userManager.SetTwoFactorEnabledAsync(foundUser, true); if (result.Succeeded) { + // Remove the UserInfo Cache + RemoveCachedUser(model.Username); + var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(foundUser, 10); return new JsonResult(new { success = true, data = recoveryCodes.ToArray() }); } @@ -386,12 +424,17 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(model.Username)) return new JsonResult(new { success = false, message = "Username is required" }); - var foundUser = await _userManager.FindByNameAsync(model.Username); + var foundUser = await GetCachedUser(model.Username); if (foundUser != null) { var result = await _userManager.SetTwoFactorEnabledAsync(foundUser, false); if (result.Succeeded) + { + // Remove the UserInfo Cache + RemoveCachedUser(model.Username); + return new JsonResult(new { success = true }); + } else return new JsonResult(new { success = false, message = "Unable to disable Two-Factor Authentication.", identityErrors = result.Errors }); } @@ -405,11 +448,14 @@ namespace Teknik.IdentityServer.Controllers if (string.IsNullOrEmpty(model.Username)) return new JsonResult(new { success = false, message = "Username is required" }); - var foundUser = await _userManager.FindByNameAsync(model.Username); + var foundUser = await GetCachedUser(model.Username); if (foundUser != null) { if (foundUser.TwoFactorEnabled) { + // Remove the UserInfo Cache + RemoveCachedUser(model.Username); + var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(foundUser, 10); return new JsonResult(new { success = true, data = recoveryCodes.ToArray() }); @@ -607,5 +653,34 @@ namespace Teknik.IdentityServer.Controllers return result.ToString().ToLowerInvariant(); } + + private async Task GetCachedUser(string username) + { + if (string.IsNullOrEmpty(username)) + throw new ArgumentNullException("username"); + + // Check the cache + string cacheKey = _UserInfoCacheKey + username; + ApplicationUser foundUser; + if (!_cache.TryGetValue(cacheKey, out foundUser)) + { + foundUser = await _userManager.FindByNameAsync(username); + if (foundUser != null) + { + _cache.AddToCache(cacheKey, foundUser, new TimeSpan(1, 0, 0)); + } + } + + return foundUser; + } + + private void RemoveCachedUser(string username) + { + if (string.IsNullOrEmpty(username)) + throw new ArgumentNullException("username"); + + string cacheKey = _UserInfoCacheKey + username; + _cache.Remove(cacheKey); + } } } \ No newline at end of file diff --git a/IdentityServer/Data/Migrations/ApplicationDb/20190129061223_UserEditDate.Designer.cs b/IdentityServer/Data/Migrations/ApplicationDb/20190129061223_UserEditDate.Designer.cs new file mode 100644 index 0000000..19c4a9c --- /dev/null +++ b/IdentityServer/Data/Migrations/ApplicationDb/20190129061223_UserEditDate.Designer.cs @@ -0,0 +1,244 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Teknik.IdentityServer; + +namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20190129061223_UserEditDate")] + partial class UserEditDate + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.4-rtm-31024") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Teknik.IdentityServer.Models.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("AccountStatus"); + + b.Property("AccountType"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("CreationDate"); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LastEdit"); + + b.Property("LastSeen"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PGPPublicKey"); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Teknik.IdentityServer.Models.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Teknik.IdentityServer.Models.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Teknik.IdentityServer.Models.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Teknik.IdentityServer.Models.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/IdentityServer/Data/Migrations/ApplicationDb/20190129061223_UserEditDate.cs b/IdentityServer/Data/Migrations/ApplicationDb/20190129061223_UserEditDate.cs new file mode 100644 index 0000000..79ad23c --- /dev/null +++ b/IdentityServer/Data/Migrations/ApplicationDb/20190129061223_UserEditDate.cs @@ -0,0 +1,24 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb +{ + public partial class UserEditDate : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "LastEdit", + table: "AspNetUsers", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "LastEdit", + table: "AspNetUsers"); + } + } +} diff --git a/IdentityServer/Data/Migrations/ApplicationDb/ApplicationDbContextModelSnapshot.cs b/IdentityServer/Data/Migrations/ApplicationDb/ApplicationDbContextModelSnapshot.cs index 379c004..492ba99 100644 --- a/IdentityServer/Data/Migrations/ApplicationDb/ApplicationDbContextModelSnapshot.cs +++ b/IdentityServer/Data/Migrations/ApplicationDb/ApplicationDbContextModelSnapshot.cs @@ -15,7 +15,7 @@ namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.2.0-preview2-35157") + .HasAnnotation("ProductVersion", "2.1.4-rtm-31024") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -150,6 +150,8 @@ namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb b.Property("EmailConfirmed"); + b.Property("LastEdit"); + b.Property("LastSeen"); b.Property("LockoutEnabled"); diff --git a/IdentityServer/Models/ApplicationUser.cs b/IdentityServer/Models/ApplicationUser.cs index f60839e..8e040b3 100644 --- a/IdentityServer/Models/ApplicationUser.cs +++ b/IdentityServer/Models/ApplicationUser.cs @@ -21,6 +21,8 @@ namespace Teknik.IdentityServer.Models public string PGPPublicKey { get; set; } + public DateTime LastEdit { get; set; } + public ApplicationUser() : base() { Init(); @@ -35,6 +37,7 @@ namespace Teknik.IdentityServer.Models { CreationDate = DateTime.Now; LastSeen = DateTime.Now; + LastEdit = DateTime.Now; AccountType = AccountType.Basic; AccountStatus = AccountStatus.Active; PGPPublicKey = null; @@ -46,6 +49,7 @@ namespace Teknik.IdentityServer.Models claims.Add(new Claim("username", UserName)); claims.Add(new Claim("creation-date", CreationDate.ToString("o"))); claims.Add(new Claim("last-seen", LastSeen.ToString("o"))); + claims.Add(new Claim("last-edit", LastEdit.ToString("o"))); claims.Add(new Claim("account-type", AccountType.ToString())); claims.Add(new Claim("account-status", AccountStatus.ToString())); claims.Add(new Claim("recovery-email", Email ?? string.Empty)); @@ -62,6 +66,7 @@ namespace Teknik.IdentityServer.Models new JProperty("username", UserName), new JProperty("creation-date", CreationDate), new JProperty("last-seen", LastSeen), + new JProperty("last-edit", LastEdit), new JProperty("account-type", AccountType), new JProperty("account-status", AccountStatus), new JProperty("recovery-email", Email), diff --git a/IdentityServer/Startup.cs b/IdentityServer/Startup.cs index e8b0856..66dc804 100644 --- a/IdentityServer/Startup.cs +++ b/IdentityServer/Startup.cs @@ -23,8 +23,7 @@ using Teknik.Logging; using Microsoft.AspNetCore.Authorization; using Teknik.IdentityServer.Models; using IdentityServer4.Services; -using System.Collections.Generic; -using Teknik.Utilities; + namespace Teknik.IdentityServer { @@ -128,6 +127,7 @@ namespace Teknik.IdentityServer options.Cors.CorsPaths.Add(new PathString("/connect/endsession")); options.Cors.CorsPaths.Add(new PathString("/connect/checksession")); options.Cors.CorsPaths.Add(new PathString("/connect/introspect")); + options.Caching.ClientStoreExpiration = TimeSpan.FromHours(1); }) .AddOperationalStore(options => options.ConfigureDbContext = builder => @@ -135,6 +135,7 @@ namespace Teknik.IdentityServer .AddConfigurationStore(options => options.ConfigureDbContext = builder => builder.UseSqlServer(config.DbConnection, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly))) + .AddConfigurationStoreCache() .AddAspNetIdentity() .AddRedirectUriValidator() .AddDeveloperSigningCredential(); diff --git a/Teknik/Areas/Admin/Views/Admin/UserSearch.cshtml b/Teknik/Areas/Admin/Views/Admin/UserSearch.cshtml index 47c68fa..ab79a24 100644 --- a/Teknik/Areas/Admin/Views/Admin/UserSearch.cshtml +++ b/Teknik/Areas/Admin/Views/Admin/UserSearch.cshtml @@ -25,4 +25,4 @@ - + diff --git a/Teknik/bundleconfig.json b/Teknik/bundleconfig.json index 564322b..7d862e6 100644 --- a/Teknik/bundleconfig.json +++ b/Teknik/bundleconfig.json @@ -20,7 +20,7 @@ { "outputFileName": "./wwwroot/js/userSearch.min.js", "inputFiles": [ - "./wwwroot/js/app/Admin/UserInfo.js" + "./wwwroot/js/app/Admin/UserSearch.js" ] }, { diff --git a/Utilities/MemoryCacheHelper.cs b/Utilities/MemoryCacheHelper.cs new file mode 100644 index 0000000..4286a1a --- /dev/null +++ b/Utilities/MemoryCacheHelper.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.Caching.Memory; + +namespace Teknik.Utilities +{ + public static class MemoryCacheHelper + { + public static bool GetCacheValue(this IMemoryCache cache, string key, out T value) + { + return cache.TryGetValue(key, out value); + } + + public static bool GetCacheValue(this IMemoryCache cache, string key, out object value) + { + return cache.TryGetValue(key, out value); + } + + public static void AddToCache(this IMemoryCache cache, string key, object value, TimeSpan expiration) + { + var cacheOptions = new MemoryCacheEntryOptions() + .SetAbsoluteExpiration(expiration); + + cache.AddToCache(key, value, cacheOptions); + } + + public static void AddToCache(this IMemoryCache cache, string key, object value, MemoryCacheEntryOptions options) + { + cache.Set(key, value, options); + } + } +}