Add CurrentUserSessionService for session ID retrieval

Introduces ICurrentUserSessionService and its implementation to provide access to the current user's session ID via the 'sid' claim. Registers the service in DI and adds comprehensive unit tests to validate correct and error scenarios.
This commit is contained in:
Artemy
2025-07-25 18:07:05 +07:00
parent ce630fbe0a
commit 751c30dedd
5 changed files with 145 additions and 1 deletions
@@ -64,6 +64,7 @@ public static class ConfigurationProgramExtensions
services.AddHttpContextAccessor(); // it's very important for CurrentUserService services.AddHttpContextAccessor(); // it's very important for CurrentUserService
services.AddScoped<ICurrentUserService, CurrentUserService>(); services.AddScoped<ICurrentUserService, CurrentUserService>();
services.AddScoped<ICurrentUserSessionService, CurrentUserSessionService>();
services.AddMemoryCache(); services.AddMemoryCache();
services.AddScoped<IPingHandlerService, PingHandlerService>(); services.AddScoped<IPingHandlerService, PingHandlerService>();
@@ -1,5 +1,6 @@
using System.Security.Claims; using System.Security.Claims;
using Govor.Application.Infrastructure.Extensions; using Govor.Application.Infrastructure.Extensions;
using Govor.Application.Interfaces.Infrastructure.Extensions;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Moq; using Moq;
@@ -9,7 +10,7 @@ namespace Govor.Application.Tests.Infrastructure.Extensions;
public class CurrentUserServiceTests public class CurrentUserServiceTests
{ {
private Mock<IHttpContextAccessor> _httpContextAccessorMock; private Mock<IHttpContextAccessor> _httpContextAccessorMock;
private CurrentUserService _currentUserService; private ICurrentUserService _currentUserService;
[SetUp] [SetUp]
public void SetUp() public void SetUp()
@@ -0,0 +1,109 @@
using System.Security.Claims;
using Govor.Application.Infrastructure.Extensions;
using Govor.Application.Interfaces.Infrastructure.Extensions;
using Microsoft.AspNetCore.Http;
using Moq;
namespace Govor.Application.Tests.Infrastructure.Extensions;
[TestFixture]
[TestOf(typeof(CurrentUserSessionService))]
public class CurrentUserSessionServiceTests
{
private Mock<IHttpContextAccessor> _httpContextAccessorMock;
private ICurrentUserSessionService _sessionService;
[SetUp]
public void SetUp()
{
_httpContextAccessorMock = new Mock<IHttpContextAccessor>();
_sessionService = new CurrentUserSessionService(_httpContextAccessorMock.Object);
}
[Test]
public void GetCurrentSessionId_ValidSidClaim_ReturnsGuid()
{
// Arrange
var sid = Guid.NewGuid();
var claims = new[] { new Claim("sid", sid.ToString()) };
var identity = new ClaimsIdentity(claims);
var principal = new ClaimsPrincipal(identity);
var httpContextMock = new Mock<HttpContext>();
httpContextMock.Setup(x => x.User).Returns(principal);
_httpContextAccessorMock.Setup(x => x.HttpContext).Returns(httpContextMock.Object);
// Act
var result = _sessionService.GetUserSessionId();
// Assert
Assert.That(result, Is.EqualTo(sid));
}
[Test]
public void GetCurrentSessionId_NoHttpContext_ThrowsUnauthorizedAccessException()
{
// Arrange
_httpContextAccessorMock.Setup(x => x.HttpContext).Returns((HttpContext)null);
// Act & Assert
var ex = Assert.Throws<UnauthorizedAccessException>(() => _sessionService.GetUserSessionId());
Assert.That(ex.Message, Is.EqualTo("Session id (sid) claim is missing or invalid"));
}
[Test]
public void GetCurrentSessionId_NoSidlaim_ThrowsUnauthorizedAccessException()
{
// Arrange
var claims = new[] { new Claim("otherClaim", "value") };
var identity = new ClaimsIdentity(claims);
var principal = new ClaimsPrincipal(identity);
var httpContextMock = new Mock<HttpContext>();
httpContextMock.Setup(x => x.User).Returns(principal);
_httpContextAccessorMock.Setup(x => x.HttpContext).Returns(httpContextMock.Object);
// Act & Assert
var ex = Assert.Throws<UnauthorizedAccessException>(() => _sessionService.GetUserSessionId());
Assert.That(ex.Message, Is.EqualTo("Session id (sid) claim is missing or invalid"));
}
[Test]
public void GetUserSessionId_InvalidSidValueClaim_ThrowsUnauthorizedAccessException()
{
// Arrange
var claims = new[] { new Claim("sid", "invalid-guid") };
var identity = new ClaimsIdentity(claims);
var principal = new ClaimsPrincipal(identity);
var httpContextMock = new Mock<HttpContext>();
httpContextMock.Setup(x => x.User).Returns(principal);
_httpContextAccessorMock.Setup(x => x.HttpContext).Returns(httpContextMock.Object);
// Act & Assert
var ex = Assert.Throws<UnauthorizedAccessException>(() => _sessionService.GetUserSessionId());
Assert.That(ex.Message, Is.EqualTo("Session id (sid) claim is missing or invalid"));
}
[Test]
public void GetUserSessionId_EmptySidValueClaim_ThrowsUnauthorizedAccessException()
{
// Arrange
var claims = new[] { new Claim("sid", "") };
var identity = new ClaimsIdentity(claims);
var principal = new ClaimsPrincipal(identity);
var httpContextMock = new Mock<HttpContext>();
httpContextMock.Setup(x => x.User).Returns(principal);
_httpContextAccessorMock.Setup(x => x.HttpContext).Returns(httpContextMock.Object);
// Act & Assert
var ex = Assert.Throws<UnauthorizedAccessException>(() => _sessionService.GetUserSessionId());
Assert.That(ex.Message, Is.EqualTo("Session id (sid) claim is missing or invalid"));
}
}
@@ -0,0 +1,27 @@
using Govor.Application.Interfaces.Infrastructure.Extensions;
using Microsoft.AspNetCore.Http;
namespace Govor.Application.Infrastructure.Extensions;
public class CurrentUserSessionService : ICurrentUserSessionService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public CurrentUserSessionService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public Guid GetUserSessionId()
{
var user = _httpContextAccessor.HttpContext?.User;
var userIdClaim = user?.FindFirst("sid")?.Value;
if (string.IsNullOrEmpty(userIdClaim) || !Guid.TryParse(userIdClaim, out var userId))
{
throw new UnauthorizedAccessException("Session id (sid) claim is missing or invalid");
}
return userId;
}
}
@@ -0,0 +1,6 @@
namespace Govor.Application.Interfaces.Infrastructure.Extensions;
public interface ICurrentUserSessionService
{
Guid GetUserSessionId();
}