diff --git a/Govor.API/Common/Extensions/ConfigurationProgramExtensions.cs b/Govor.API/Common/Extensions/ConfigurationProgramExtensions.cs index 4795c58..58c622f 100644 --- a/Govor.API/Common/Extensions/ConfigurationProgramExtensions.cs +++ b/Govor.API/Common/Extensions/ConfigurationProgramExtensions.cs @@ -64,6 +64,7 @@ public static class ConfigurationProgramExtensions services.AddHttpContextAccessor(); // it's very important for CurrentUserService services.AddScoped(); + services.AddScoped(); services.AddMemoryCache(); services.AddScoped(); diff --git a/Govor.Application.Tests/Infrastructure/Extensions/CurrentUserServiceTests.cs b/Govor.Application.Tests/Infrastructure/Extensions/CurrentUserServiceTests.cs index 69b8fbc..31fc66b 100644 --- a/Govor.Application.Tests/Infrastructure/Extensions/CurrentUserServiceTests.cs +++ b/Govor.Application.Tests/Infrastructure/Extensions/CurrentUserServiceTests.cs @@ -1,5 +1,6 @@ using System.Security.Claims; using Govor.Application.Infrastructure.Extensions; +using Govor.Application.Interfaces.Infrastructure.Extensions; using Microsoft.AspNetCore.Http; using Moq; @@ -9,7 +10,7 @@ namespace Govor.Application.Tests.Infrastructure.Extensions; public class CurrentUserServiceTests { private Mock _httpContextAccessorMock; - private CurrentUserService _currentUserService; + private ICurrentUserService _currentUserService; [SetUp] public void SetUp() diff --git a/Govor.Application.Tests/Infrastructure/Extensions/CurrentUserSessionServiceTests.cs b/Govor.Application.Tests/Infrastructure/Extensions/CurrentUserSessionServiceTests.cs new file mode 100644 index 0000000..c9c42e5 --- /dev/null +++ b/Govor.Application.Tests/Infrastructure/Extensions/CurrentUserSessionServiceTests.cs @@ -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 _httpContextAccessorMock; + private ICurrentUserSessionService _sessionService; + + [SetUp] + public void SetUp() + { + _httpContextAccessorMock = new Mock(); + _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(); + 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(() => _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(); + httpContextMock.Setup(x => x.User).Returns(principal); + + _httpContextAccessorMock.Setup(x => x.HttpContext).Returns(httpContextMock.Object); + + // Act & Assert + var ex = Assert.Throws(() => _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(); + httpContextMock.Setup(x => x.User).Returns(principal); + + _httpContextAccessorMock.Setup(x => x.HttpContext).Returns(httpContextMock.Object); + + // Act & Assert + var ex = Assert.Throws(() => _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(); + httpContextMock.Setup(x => x.User).Returns(principal); + + _httpContextAccessorMock.Setup(x => x.HttpContext).Returns(httpContextMock.Object); + + // Act & Assert + var ex = Assert.Throws(() => _sessionService.GetUserSessionId()); + Assert.That(ex.Message, Is.EqualTo("Session id (sid) claim is missing or invalid")); + } +} \ No newline at end of file diff --git a/Govor.Application/Infrastructure/Extensions/CurrentUserSessionService.cs b/Govor.Application/Infrastructure/Extensions/CurrentUserSessionService.cs new file mode 100644 index 0000000..f986ab4 --- /dev/null +++ b/Govor.Application/Infrastructure/Extensions/CurrentUserSessionService.cs @@ -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; + } +} \ No newline at end of file diff --git a/Govor.Application/Interfaces/Infrastructure/Extensions/ICurrentUserSessionService.cs b/Govor.Application/Interfaces/Infrastructure/Extensions/ICurrentUserSessionService.cs new file mode 100644 index 0000000..f4000a9 --- /dev/null +++ b/Govor.Application/Interfaces/Infrastructure/Extensions/ICurrentUserSessionService.cs @@ -0,0 +1,6 @@ +namespace Govor.Application.Interfaces.Infrastructure.Extensions; + +public interface ICurrentUserSessionService +{ + Guid GetUserSessionId(); +} \ No newline at end of file