Skip to content

Commit fee8da7

Browse files
committed
(#272) [.Net 9] Add HybridCache
1 parent 8003fa5 commit fee8da7

File tree

28 files changed

+246
-19
lines changed

28 files changed

+246
-19
lines changed

src/Microservices/Common/ClassifiedAds.Infrastructure/Caching/CachingServiceCollectionExtensions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ public static IServiceCollection AddCaches(this IServiceCollection services, Cac
3838
});
3939
}
4040

41+
services.AddHybridCache(options =>
42+
{
43+
});
44+
4145
return services;
4246
}
4347
}

src/Microservices/Common/ClassifiedAds.Infrastructure/ClassifiedAds.Infrastructure.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@
3434
<PackageReference Include="Microsoft.Azure.AppConfiguration.AspNetCore" Version="8.0.0" />
3535
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
3636
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.1" />
37+
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="9.4.0" />
3738
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.3.0" />
3839
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="9.0.1" />
39-
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.1" />
4040
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="9.0.1" />
4141
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="9.0.1" />
4242
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.11.0" />

src/Microservices/Common/ClassifiedAds.Infrastructure/Web/Authorization/Policies/PolicyServiceCollectionExtensions.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using ClassifiedAds.Infrastructure.Web.Authorization.Requirements;
2+
using ClassifiedAds.Infrastructure.Web.ClaimsTransformations;
3+
using Microsoft.AspNetCore.Authentication;
24
using Microsoft.AspNetCore.Authorization;
35
using System;
46
using System.Collections.Generic;
@@ -11,6 +13,11 @@ public static class PolicyServiceCollectionExtensions
1113
{
1214
public static IServiceCollection AddAuthorizationPolicies(this IServiceCollection services, Assembly assembly, IEnumerable<string> policies)
1315
{
16+
if (!services.Any(s => s.ServiceType == typeof(IClaimsTransformation) && s.ImplementationType == typeof(CustomClaimsTransformation)))
17+
{
18+
services.AddSingleton<IClaimsTransformation, CustomClaimsTransformation>();
19+
}
20+
1421
services.Configure<AuthorizationOptions>(options =>
1522
{
1623
foreach (var policyName in policies)
@@ -27,7 +34,7 @@ public static IServiceCollection AddAuthorizationPolicies(this IServiceCollectio
2734

2835
if (!services.Any(s => s.ServiceType == typeof(IAuthorizationHandler) && s.ImplementationType == typeof(PermissionRequirementHandler)))
2936
{
30-
services.AddSingleton(typeof(IAuthorizationHandler), typeof(PermissionRequirementHandler));
37+
services.AddSingleton<IAuthorizationHandler, PermissionRequirementHandler>();
3138
}
3239

3340
var requirementHandlerTypes = assembly.GetTypes()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using Microsoft.AspNetCore.Authentication;
2+
using Microsoft.Extensions.Caching.Hybrid;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Security.Claims;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
10+
namespace ClassifiedAds.Infrastructure.Web.ClaimsTransformations;
11+
12+
public class CustomClaimsTransformation : IClaimsTransformation
13+
{
14+
private readonly HybridCache _cache;
15+
16+
public CustomClaimsTransformation(HybridCache cache)
17+
{
18+
_cache = cache;
19+
}
20+
21+
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
22+
{
23+
var identity = principal.Identities.FirstOrDefault(x => x.IsAuthenticated);
24+
if (identity == null)
25+
{
26+
return principal;
27+
}
28+
29+
var userClaim = principal.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier);
30+
31+
if (Guid.TryParse(userClaim?.Value, out var userId))
32+
{
33+
var issuedAt = principal.Claims.FirstOrDefault(x => x.Type == "iat").Value;
34+
35+
var cacheKey = $"permissions/{userId}/{issuedAt}";
36+
37+
var permissions = await _cache.GetOrCreateAsync(cacheKey,
38+
async (cancellationToken) => await GetPermissionsAsync(userId, cancellationToken),
39+
tags: ["permissions", $"permissions/{userId}"]);
40+
41+
var claims = new List<Claim>();
42+
claims.AddRange(permissions.Select(p => new Claim("Permission", p)));
43+
claims.AddRange(principal.Claims);
44+
45+
var newIdentity = new ClaimsIdentity(claims, identity.AuthenticationType);
46+
return new ClaimsPrincipal(newIdentity);
47+
}
48+
49+
return principal;
50+
}
51+
52+
private Task<List<string>> GetPermissionsAsync(Guid userId, CancellationToken cancellationToken)
53+
{
54+
// TODO: Get from Db
55+
var claims = new List<string>();
56+
return Task.FromResult(claims);
57+
}
58+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313

1414
namespace Microsoft.Extensions.DependencyInjection;
1515

16-
public static class AuditLogModuleServiceCollectionExtensions
16+
public static class ServiceCollectionExtensions
1717
{
1818
public static IServiceCollection AddAuditLogModule(this IServiceCollection services, AppSettings appSettings)
1919
{
20+
services.AddCaches(appSettings.Caching);
21+
2022
services.AddDbContext<AuditLogDbContext>(options => options.UseSqlServer(appSettings.ConnectionStrings.ClassifiedAds, sql =>
2123
{
2224
if (!string.IsNullOrEmpty(appSettings.ConnectionStrings.MigrationsAssembly))
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313

1414
namespace Microsoft.Extensions.DependencyInjection;
1515

16-
public static class ConfigurationModuleServiceCollectionExtensions
16+
public static class ServiceCollectionExtensions
1717
{
1818
public static IServiceCollection AddConfigurationModule(this IServiceCollection services, AppSettings appSettings)
1919
{
20+
services.AddCaches(appSettings.Caching);
21+
2022
services.AddDbContext<ConfigurationDbContext>(options => options.UseSqlServer(appSettings.ConnectionStrings.ClassifiedAds, sql =>
2123
{
2224
if (!string.IsNullOrEmpty(appSettings.ConnectionStrings.MigrationsAssembly))
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515

1616
namespace Microsoft.Extensions.DependencyInjection;
1717

18-
public static class IdentityModuleServiceCollectionExtensions
18+
public static class ServiceCollectionExtensions
1919
{
2020
public static IServiceCollection AddIdentityModule(this IServiceCollection services, AppSettings appSettings)
2121
{
22+
services.AddCaches(appSettings.Caching);
23+
2224
services.AddDbContext<IdentityDbContext>(options => options.UseSqlServer(appSettings.ConnectionStrings.ClassifiedAds, sql =>
2325
{
2426
if (!string.IsNullOrEmpty(appSettings.ConnectionStrings.MigrationsAssembly))
@@ -50,6 +52,8 @@ public static IServiceCollection AddIdentityModule(this IServiceCollection servi
5052

5153
public static IServiceCollection AddIdentityModuleCore(this IServiceCollection services, AppSettings appSettings)
5254
{
55+
services.AddCaches(appSettings.Caching);
56+
5357
services.AddDbContext<IdentityDbContext>(options => options.UseSqlServer(appSettings.ConnectionStrings.ClassifiedAds, sql =>
5458
{
5559
if (!string.IsNullOrEmpty(appSettings.ConnectionStrings.MigrationsAssembly))
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88

99
namespace Microsoft.Extensions.DependencyInjection;
1010

11-
public static class NotificationModuleServiceCollectionExtensions
11+
public static class ServiceCollectionExtensions
1212
{
1313
public static IServiceCollection AddNotificationModule(this IServiceCollection services, AppSettings appSettings)
1414
{
15+
services.AddCaches(appSettings.Caching);
16+
1517
services
1618
.AddDbContext<NotificationDbContext>(options => options.UseSqlServer(appSettings.ConnectionStrings.ClassifiedAds, sql =>
1719
{
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222

2323
namespace Microsoft.Extensions.DependencyInjection;
2424

25-
public static class ProductModuleServiceCollectionExtensions
25+
public static class ServiceCollectionExtensions
2626
{
2727
public static IServiceCollection AddProductModule(this IServiceCollection services, AppSettings appSettings)
2828
{
29+
services.AddCaches(appSettings.Caching);
30+
2931
services.AddDbContext<ProductDbContext>(options => options.UseSqlServer(appSettings.ConnectionStrings.ClassifiedAds, sql =>
3032
{
3133
if (!string.IsNullOrEmpty(appSettings.ConnectionStrings.MigrationsAssembly))
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717

1818
namespace Microsoft.Extensions.DependencyInjection;
1919

20-
public static class StorageModuleServiceCollectionExtensions
20+
public static class ServiceCollectionExtensions
2121
{
2222
public static IServiceCollection AddStorageModule(this IServiceCollection services, AppSettings appSettings)
2323
{
24+
services.AddCaches(appSettings.Caching);
25+
2426
services.AddDbContext<StorageDbContext>(options => options.UseSqlServer(appSettings.ConnectionStrings.ClassifiedAds, sql =>
2527
{
2628
if (!string.IsNullOrEmpty(appSettings.ConnectionStrings.MigrationsAssembly))

src/ModularMonolith/ClassifiedAds.Background/ConfigurationOptions/AppSettings.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using ClassifiedAds.Infrastructure.Logging;
1+
using ClassifiedAds.Infrastructure.Caching;
2+
using ClassifiedAds.Infrastructure.Logging;
23
using ClassifiedAds.Infrastructure.MessageBrokers;
34
using ClassifiedAds.Infrastructure.Monitoring;
45
using Microsoft.Extensions.Options;
@@ -9,6 +10,8 @@ public class AppSettings
910
{
1011
public LoggingOptions Logging { get; set; }
1112

13+
public CachingOptions Caching { get; set; }
14+
1215
public MonitoringOptions Monitoring { get; set; }
1316

1417
public MessageBrokerOptions MessageBroker { get; set; }

src/ModularMonolith/ClassifiedAds.Background/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343

4444
services.AddDateTimeProvider();
4545

46+
services.AddCaches(appSettings.Caching);
47+
4648
services
4749
.AddAuditLogModule(opt => configuration.GetSection("Modules:AuditLog").Bind(opt))
4850
.AddIdentityModuleCore(opt => configuration.GetSection("Modules:Identity").Bind(opt))

src/ModularMonolith/ClassifiedAds.Infrastructure/Caching/CachingServiceCollectionExtensions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ public static IServiceCollection AddCaches(this IServiceCollection services, Cac
3838
});
3939
}
4040

41+
services.AddHybridCache(options =>
42+
{
43+
});
44+
4145
return services;
4246
}
4347
}

src/ModularMonolith/ClassifiedAds.Infrastructure/ClassifiedAds.Infrastructure.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@
3232
<PackageReference Include="Microsoft.Azure.AppConfiguration.AspNetCore" Version="8.0.0" />
3333
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
3434
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.1" />
35+
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="9.4.0" />
3536
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.3.0" />
3637
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="9.0.1" />
37-
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.1" />
3838
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="9.0.1" />
3939
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="9.0.1" />
4040
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.11.0" />

src/ModularMonolith/ClassifiedAds.Infrastructure/Web/Authorization/Policies/PolicyServiceCollectionExtensions.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using ClassifiedAds.Infrastructure.Web.Authorization.Requirements;
2+
using ClassifiedAds.Infrastructure.Web.ClaimsTransformations;
3+
using Microsoft.AspNetCore.Authentication;
24
using Microsoft.AspNetCore.Authorization;
35
using System;
46
using System.Collections.Generic;
@@ -11,6 +13,11 @@ public static class PolicyServiceCollectionExtensions
1113
{
1214
public static IServiceCollection AddAuthorizationPolicies(this IServiceCollection services, Assembly assembly, IEnumerable<string> policies)
1315
{
16+
if (!services.Any(s => s.ServiceType == typeof(IClaimsTransformation) && s.ImplementationType == typeof(CustomClaimsTransformation)))
17+
{
18+
services.AddSingleton<IClaimsTransformation, CustomClaimsTransformation>();
19+
}
20+
1421
services.Configure<AuthorizationOptions>(options =>
1522
{
1623
foreach (var policyName in policies)
@@ -27,7 +34,7 @@ public static IServiceCollection AddAuthorizationPolicies(this IServiceCollectio
2734

2835
if (!services.Any(s => s.ServiceType == typeof(IAuthorizationHandler) && s.ImplementationType == typeof(PermissionRequirementHandler)))
2936
{
30-
services.AddSingleton(typeof(IAuthorizationHandler), typeof(PermissionRequirementHandler));
37+
services.AddSingleton<IAuthorizationHandler, PermissionRequirementHandler>();
3138
}
3239

3340
var requirementHandlerTypes = assembly.GetTypes()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using Microsoft.AspNetCore.Authentication;
2+
using Microsoft.Extensions.Caching.Hybrid;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Security.Claims;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
10+
namespace ClassifiedAds.Infrastructure.Web.ClaimsTransformations;
11+
12+
public class CustomClaimsTransformation : IClaimsTransformation
13+
{
14+
private readonly HybridCache _cache;
15+
16+
public CustomClaimsTransformation(HybridCache cache)
17+
{
18+
_cache = cache;
19+
}
20+
21+
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
22+
{
23+
var identity = principal.Identities.FirstOrDefault(x => x.IsAuthenticated);
24+
if (identity == null)
25+
{
26+
return principal;
27+
}
28+
29+
var userClaim = principal.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier);
30+
31+
if (Guid.TryParse(userClaim?.Value, out var userId))
32+
{
33+
var issuedAt = principal.Claims.FirstOrDefault(x => x.Type == "iat").Value;
34+
35+
var cacheKey = $"permissions/{userId}/{issuedAt}";
36+
37+
var permissions = await _cache.GetOrCreateAsync(cacheKey,
38+
async (cancellationToken) => await GetPermissionsAsync(userId, cancellationToken),
39+
tags: ["permissions", $"permissions/{userId}"]);
40+
41+
var claims = new List<Claim>();
42+
claims.AddRange(permissions.Select(p => new Claim("Permission", p)));
43+
claims.AddRange(principal.Claims);
44+
45+
var newIdentity = new ClaimsIdentity(claims, identity.AuthenticationType);
46+
return new ClaimsPrincipal(newIdentity);
47+
}
48+
49+
return principal;
50+
}
51+
52+
private Task<List<string>> GetPermissionsAsync(Guid userId, CancellationToken cancellationToken)
53+
{
54+
// TODO: Get from Db
55+
var claims = new List<string>();
56+
return Task.FromResult(claims);
57+
}
58+
}

src/ModularMonolith/ClassifiedAds.Migrator/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929

3030
services.AddDateTimeProvider();
3131

32+
services.AddCaches();
33+
3234
services.AddAuditLogModule(opt =>
3335
{
3436
configuration.GetSection("Modules:AuditLog").Bind(opt);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
namespace Microsoft.Extensions.DependencyInjection;
1717

18-
public static class AuditLogModuleServiceCollectionExtensions
18+
public static class ServiceCollectionExtensions
1919
{
2020
public static IServiceCollection AddAuditLogModule(this IServiceCollection services, Action<AuditLogModuleOptions> configureOptions)
2121
{
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
namespace Microsoft.Extensions.DependencyInjection;
1616

17-
public static class ConfigurationModuleServiceCollectionExtensions
17+
public static class ServiceCollectionExtensions
1818
{
1919
public static IServiceCollection AddConfigurationModule(this IServiceCollection services, Action<ConfigurationModuleOptions> configureOptions)
2020
{
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
namespace Microsoft.Extensions.DependencyInjection;
2020

21-
public static class IdentityModuleServiceCollectionExtensions
21+
public static class ServiceCollectionExtensions
2222
{
2323
public static IServiceCollection AddIdentityModule(this IServiceCollection services, Action<IdentityModuleOptions> configureOptions)
2424
{
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
namespace Microsoft.Extensions.DependencyInjection;
1515

16-
public static class NotificationModuleServiceCollectionExtensions
16+
public static class ServiceCollectionExtensions
1717
{
1818
public static IServiceCollection AddNotificationModule(this IServiceCollection services, Action<NotificationModuleOptions> configureOptions)
1919
{
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
namespace Microsoft.Extensions.DependencyInjection;
2323

24-
public static class ProductModuleServiceCollectionExtensions
24+
public static class ServiceCollectionExtensions
2525
{
2626
public static IServiceCollection AddProductModule(this IServiceCollection services, Action<ProductModuleOptions> configureOptions)
2727
{
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
namespace Microsoft.Extensions.DependencyInjection;
1818

19-
public static class StorageModuleServiceCollectionExtensions
19+
public static class ServiceCollectionExtensions
2020
{
2121
public static IServiceCollection AddStorageModule(this IServiceCollection services, Action<StorageModuleOptions> configureOptions)
2222
{

0 commit comments

Comments
 (0)