Skip to content

Commit 7325bfa

Browse files
redis endpoint info added
1 parent 7df5de0 commit 7325bfa

21 files changed

+601
-68
lines changed

CentralizedLogging.Sdk/CentralizedLogging.Sdk.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
<ItemGroup>
1010
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.3.0" />
1111
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.0" />
12-
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.8" />
12+
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.10" />
1313
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.8" />
1414
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.8" />
15-
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.9" />
15+
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.10" />
1616
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.8" />
1717
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="9.0.8" />
18-
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.8" />
18+
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.10" />
1919
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
2020
<PackageReference Include="Polly" Version="8.6.3" />
2121
<PackageReference Include="Polly.Extensions.Http" Version="3.0.0" />

CentralizedLogging.Sdk/CentralizedLoggingClient.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using CentralizedLogging.Contracts.Models;
33
using CentralizedLogging.Sdk.Abstractions;
44
using Microsoft.AspNetCore.Mvc;
5+
using SharedLibrary;
6+
using System.Net;
57
using System.Net.Http.Json;
68
using System.Text.Json;
79

@@ -15,8 +17,17 @@ internal sealed class CentralizedLoggingClient : ICentralizedLoggingClient
1517

1618
public async Task<List<GetAllErrorsResponseModel>> GetAllErrorAsync(CancellationToken ct = default)
1719
{
18-
var resp = await _http.GetFromJsonAsync<List<GetAllErrorsResponseModel>>("api/errorlogs");
19-
return resp;
20+
var request = new HttpRequestMessage(HttpMethod.Get, "api/errorlogs");
21+
var response = await _http.SendAsync(request, ct);
22+
23+
if (response.StatusCode is HttpStatusCode.Forbidden or HttpStatusCode.Unauthorized)
24+
{
25+
throw new PermissionDeniedException((int)response.StatusCode);
26+
}
27+
28+
response.EnsureSuccessStatusCode(); // for 200 OK only
29+
30+
return await response.Content.ReadFromJsonAsync<List<GetAllErrorsResponseModel>>(cancellationToken: ct);
2031
}
2132

2233
public async Task LogErrorAsync(CreateErrorLogDto request, CancellationToken ct)

CentralizedLogging.Sdk/Extensions/ServiceCollectionExtensions.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,6 @@ public static IServiceCollection AddCentralizedLoggingSdk(
2626
if (configure is not null)
2727
services.PostConfigure(configure);
2828

29-
// Delegating handler MUST be transient
30-
services.AddTransient<BearerTokenHandler>();
31-
32-
// Register the token provider (memory-based)
33-
services.AddScoped<ICacheAccessProvider, CacheAccessProvider>();
34-
35-
36-
3729
// The Typed client
3830
services.AddHttpClient<ICentralizedLoggingClient, CentralizedLoggingClient>((sp, http) =>
3931
{

CentralizedLoggingApi/Controllers/ErrorLogsController.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.EntityFrameworkCore;
77
using CentralizedLogging.Contracts.Models;
88
using CentralizedLogging.Contracts.DTO;
9+
using SharedLibrary.Auth;
910

1011
namespace CentralizedLoggingApi.Controllers
1112
{
@@ -61,7 +62,7 @@ public async Task<IActionResult> GetErrorById(long id)
6162
return Ok(error);
6263
}
6364

64-
[Authorize]
65+
[Authorize(Policy = PolicyType.API_LEVEL)]
6566
// GET api/errorlogs
6667
[HttpGet]
6768
public async Task<IActionResult> GetAllErrors()

CentralizedLoggingApi/appsettings.Development.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
{
2+
"Redis": {
3+
"Endpoint": "localhost:6379",
4+
"Password": "Bisp@123"
5+
},
26
"JAEGER_HOST": "localhost",
37
"ConnectionStrings": {
48
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=CentralizedLoggingDB;Trusted_Connection=True;MultipleActiveResultSets=true",

CentralizedLoggingApi/appsettings.Production.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
{
2+
"Redis": {
3+
"Endpoint": "dragonfly:6379",
4+
"Password": "Bisp@123"
5+
},
26
"JAEGER_HOST": "jaeger",
37
"ConnectionStrings": {
48
"DefaultConnection": "Server=db,1433;Database=CentralizedLoggingDB;User=sa;Password=Bisp@123;MultipleActiveResultSets=true;TrustServerCertificate=True;Encrypt=False;",
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using Microsoft.AspNetCore.Authorization;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Routing;
4+
using Microsoft.Extensions.Caching.Distributed;
5+
using SharedLibrary.Cache;
6+
using System.Text.Json;
7+
using UserManagementApi.Contracts.Models;
8+
using static System.Net.WebRequestMethods;
9+
10+
namespace SharedLibrary.Auth
11+
{
12+
public sealed class PermissionHandler : AuthorizationHandler<PermissionRequirement>
13+
{
14+
private readonly IHttpContextAccessor _http;
15+
private readonly IDistributedCache _cache;
16+
private readonly ICacheAccessProvider _tokens;
17+
18+
public PermissionHandler(IHttpContextAccessor http, IDistributedCache cache, ICacheAccessProvider tokens) => (_http, _cache, _tokens) = (http, cache, tokens);
19+
20+
protected override async Task HandleRequirementAsync(
21+
AuthorizationHandlerContext context, PermissionRequirement requirement)
22+
{
23+
var userId =
24+
context.User.FindFirst("sub")?.Value ??
25+
context.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
26+
27+
if (string.IsNullOrEmpty(userId))
28+
return; // no user → no success
29+
30+
var rd = _http.HttpContext!.GetRouteData();
31+
var area = rd.Values["area"]?.ToString() ?? "";
32+
var controller = rd.Values["controller"]?.ToString() ?? "";
33+
var action = rd.Values["action"]?.ToString() ?? "";
34+
35+
var ct = _http.HttpContext?.RequestAborted ?? default;
36+
var permissions = await _tokens.GetUserPermissionsAsync(ct);
37+
38+
IReadOnlyList<Category> categories = new List<Category>();
39+
if (permissions != null)
40+
{
41+
categories = JsonSerializer.Deserialize<List<Category>>(permissions);
42+
}
43+
44+
if (categories is null) return;
45+
46+
bool IsAllowed = categories.Any( c => c.Modules.Any( m => m.Area == area && m.Controller == controller && m.Action == action));
47+
48+
49+
if (IsAllowed)
50+
context.Succeed(requirement);
51+
}
52+
}
53+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using Microsoft.AspNetCore.Authorization;
2+
3+
namespace SharedLibrary.Auth
4+
{
5+
public sealed class PermissionRequirement : IAuthorizationRequirement
6+
{
7+
public string Permission { get; }
8+
public PermissionRequirement(string permission) => Permission = permission;
9+
}
10+
}

SharedLibrary/Auth/PolicyType.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection.Metadata;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace SharedLibrary.Auth
9+
{
10+
public static class PolicyType
11+
{
12+
public const string WEB_LEVEL = "WebPolicy";
13+
public const string API_LEVEL = "APIPolicy";
14+
}
15+
}

SharedLibrary/Cache/CacheAccessProvider.cs

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,84 @@
11
using Microsoft.AspNetCore.Http;
2-
using Microsoft.Extensions.Caching.Memory;
2+
using Microsoft.Extensions.Caching.Distributed;
33
using System.Security.Claims;
44

55
namespace SharedLibrary.Cache
66
{
77
public sealed class CacheAccessProvider : ICacheAccessProvider
88
{
9-
private readonly IMemoryCache _cache;
9+
private readonly IDistributedCache _cache;
1010
private readonly IHttpContextAccessor _http;
1111

12-
public CacheAccessProvider(IMemoryCache cache, IHttpContextAccessor http)
12+
public CacheAccessProvider(IDistributedCache cache, IHttpContextAccessor http)
1313
{
1414
_cache = cache;
1515
_http = http;
1616
}
1717

18-
public Task<string?> GetAccessTokenAsync(CancellationToken ct = default)
18+
public async Task<string?> GetAccessTokenAsync(CancellationToken ct = default)
1919
{
2020
var user = _http.HttpContext?.User;
2121
var uid =
2222
user?.FindFirst("sub")?.Value
2323
?? user?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
2424

25-
if (string.IsNullOrEmpty(uid)) return Task.FromResult<string?>(null);
25+
if (string.IsNullOrEmpty(uid)) return null;
2626

27-
var key = $"token:{uid}";
28-
_cache.TryGetValue(key, out string? token);
29-
return Task.FromResult(token);
27+
var key = $"auth:token:{uid}";
28+
var token = await _cache.GetStringAsync(key, ct);
29+
return token;
30+
3031
}
3132

32-
public Task<string?> GetUserPermissionsAsync(CancellationToken ct = default)
33+
public async Task<string?> GetUserPermissionsAsync(CancellationToken ct = default)
3334
{
3435
var user = _http.HttpContext?.User;
3536
var uid =
3637
user?.FindFirst("sub")?.Value
3738
?? user?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
3839

39-
if (string.IsNullOrEmpty(uid)) return Task.FromResult<string?>(null);
40+
if (string.IsNullOrEmpty(uid)) return null;
4041

41-
var key = $"perm:{uid}";
42-
_cache.TryGetValue(key, out string? permissions);
43-
return Task.FromResult(permissions);
42+
var key = $"auth:permissions:{uid}";
43+
var permissions = await _cache.GetStringAsync(key, ct);
44+
return permissions;
4445
}
4546

4647
// Optional helper method to set the token into cache
47-
public void SetAccessToken(string token, int userId, DateTime expiresAtUtc)
48-
{
49-
var ttl = ToTtl(expiresAtUtc);
50-
_cache.Set($"token:{userId}", token, ttl);
48+
public async Task SetAccessToken(string token, int userId, DateTime expiresAtUtc, CancellationToken ct = default)
49+
{
50+
var ttl = ToTtl(expiresAtUtc);
51+
52+
var options = new DistributedCacheEntryOptions
53+
{
54+
AbsoluteExpirationRelativeToNow = ttl
55+
};
56+
57+
// IMPORTANT: Do not manually prefix here if using InstanceName in startup.
58+
var key = $"auth:token:{userId}";
59+
60+
await _cache.SetStringAsync(key, token, options, ct);
5161
}
5262

53-
public void SetUserPermissions(string permissions, int userId, DateTime expiresAtUtc)
63+
public async Task SetUserPermissions(string permissions, int userId, DateTime expiresAtUtc, CancellationToken ct = default)
5464
{
5565
var ttl = ToTtl(expiresAtUtc);
56-
_cache.Set($"perm:{userId}", permissions, ttl);
66+
67+
var options = new DistributedCacheEntryOptions
68+
{
69+
AbsoluteExpirationRelativeToNow = ttl
70+
};
71+
72+
// IMPORTANT: Do not manually prefix here if using InstanceName in startup.
73+
var key = $"auth:permissions:{userId}";
74+
75+
await _cache.SetStringAsync(key, permissions, options, ct);
5776
}
5877

5978
public Task RemoveAsync(string userId, CancellationToken ct = default)
6079
{
61-
_cache.Remove($"token:{userId}");
80+
_cache.Remove($"auth:token:{userId}");
81+
_cache.Remove($"auth:permissions:{userId}");
6282
return Task.CompletedTask;
6383
}
6484

0 commit comments

Comments
 (0)