diff --git a/Govor.API.Tests/IntegrationTests/Controllers/ProfileControllerTests.cs b/Govor.API.Tests/IntegrationTests/Controllers/ProfileControllerTests.cs index 323d590..1e73e2d 100644 --- a/Govor.API.Tests/IntegrationTests/Controllers/ProfileControllerTests.cs +++ b/Govor.API.Tests/IntegrationTests/Controllers/ProfileControllerTests.cs @@ -157,16 +157,6 @@ private Mock> _mockLogger = null!; )), Times.Once); _mockProfileService.Verify(s => s.SetNewIcon(_userId, mediaId), Times.Once); - - _mockClientProxy.Verify( - c => c.SendCoreAsync("AvatarUpdated", It.Is( - args => args.Length == 1 && - JObject.FromObject(args[0]!).Value("userId") == _userId && - JObject.FromObject(args[0]!).Value("iconId") == mediaId - ), It.IsAny()), - Times.Once, - "Hub SendAsync must be called to notify clients." - ); } [Test] diff --git a/Govor.API/Controllers/ProfileController.cs b/Govor.API/Controllers/ProfileController.cs index 3ce3b87..23b4053 100644 --- a/Govor.API/Controllers/ProfileController.cs +++ b/Govor.API/Controllers/ProfileController.cs @@ -70,7 +70,6 @@ public class ProfileController : ControllerBase var mediaInfo = await _mediaService.UploadMediaAsync(media); await _profileService.SetNewIcon(userId, mediaInfo.MediaId); - var iconId = mediaInfo.MediaId; return Ok(mediaInfo); } diff --git a/Govor.API/Program.cs b/Govor.API/Program.cs index 2e99680..4db6d4d 100644 --- a/Govor.API/Program.cs +++ b/Govor.API/Program.cs @@ -3,6 +3,7 @@ using Govor.API.Common.Extensions; using Govor.API.Hubs; using Govor.Application.Services.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; @@ -13,7 +14,12 @@ var services = builder.Services; builder.AddLogger();// Serilog +#if DEBUG +builder.Configuration.AddJsonFile("appsettings.Development.json", optional: false, reloadOnChange: true); +#else builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); +#endif + builder.Services.AddCors(options => { @@ -125,6 +131,9 @@ app.UseAuthorization(); app.MapControllers(); +app.Map("/server/ping", + () => new OkResult()); + app.MapHub("/hubs/chats"); app.MapHub("/hubs/friends"); app.MapHub("/hubs/profiles"); diff --git a/Govor.API/Properties/launchSettings.json b/Govor.API/Properties/launchSettings.json index defffb2..bdcb24a 100644 --- a/Govor.API/Properties/launchSettings.json +++ b/Govor.API/Properties/launchSettings.json @@ -5,7 +5,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": false, - "applicationUrl": "http://0.0.0.0:8080;", + "applicationUrl": "http://0.0.0.0:8080;http://localhost:7155", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/Govor.API/appsettings.Development.json b/Govor.API/appsettings.Development.json index 0c208ae..61c97cc 100644 --- a/Govor.API/appsettings.Development.json +++ b/Govor.API/appsettings.Development.json @@ -4,5 +4,20 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } + }, + "ConnectionStrings": { + "GovorDbContext": "Host=localhost;Port=5432;Database=GovorDb;Username=postgres;Password=stalcker;" + }, + "UseMySql": false, + "AllowedHosts": "*", + "JwtAccessOption": { + "SecretKey": "Q89eY7zP7C4+TqLmHF4kw9xkF1E8Ru4Zpg+up9wFt9g=", + "Minutes": 10 + }, + "JwtRefreshOption": { + "RefreshTokenLifetimeDays": 30 + }, + "EncryptionOption": { + "Secret": "8B2j9kkw9xP5m7nQwE2zY3A-=Q8zP7C4+TqLZpg" } } diff --git a/Govor.Application.Tests/Services/UserSessions/UserSessionRefresherTests.cs b/Govor.Application.Tests/Services/UserSessions/UserSessionRefresherTests.cs index 4fc2e6b..b717f9f 100644 --- a/Govor.Application.Tests/Services/UserSessions/UserSessionRefresherTests.cs +++ b/Govor.Application.Tests/Services/UserSessions/UserSessionRefresherTests.cs @@ -75,23 +75,30 @@ public class UserSessionRefresherTests { // Arrange _sessionsRepoMock.Setup(r => r.GetByHashedRefreshTokenAsync(OldRefreshToken)).ReturnsAsync(_session); + _jwtTokenHasherMock.Setup(h => h.HashToken(It.IsAny())).Returns(OldRefreshToken); _usersRepoMock.Setup(r => r.FindByIdAsync(_user.Id)).ReturnsAsync(_user); _jwtServiceMock.Setup(j => j.GenerateAccessTokenAsync(_user, _session.Id)).ReturnsAsync(NewAccessToken); _jwtServiceMock.Setup(j => j.GenerateRefreshTokenAsync(_user)).ReturnsAsync(NewRefreshToken); - + // Act var result = await _refresher.RefreshTokenAsync(OldRefreshToken); // Assert - Assert.That(result.accessToken, Is.EqualTo(NewAccessToken)); - Assert.That(result.refreshToken, Is.EqualTo(NewRefreshToken)); - Assert.That(_session.IsRevoked, Is.True); + Assert.Multiple(() => + { + Assert.That(result.accessToken, Is.EqualTo(NewAccessToken)); + Assert.That(result.refreshToken, Is.EqualTo(NewRefreshToken)); + }); - _sessionsRepoMock.Verify(r => r.UpdateAsync(_session), Times.Once); - _sessionsRepoMock.Verify(r => r.AddAsync(It.Is(s => - s.UserId == _user.Id && - s.RefreshTokenHash == NewRefreshToken && - s.DeviceInfo == _session.DeviceInfo)), Times.Once); + _sessionsRepoMock.Verify( + r => r.UpdateAsync( + It.Is(s => + s.Id == _session.Id && + s.UserId == _user.Id && + s.IsRevoked == false + )), + Times.Once); + _sessionsRepoMock.Verify(r => r.AddAsync(It.IsAny()), Times.Never); } [Test] @@ -100,6 +107,7 @@ public class UserSessionRefresherTests // Arrange _session.IsRevoked = true; _sessionsRepoMock.Setup(r => r.GetByHashedRefreshTokenAsync(OldRefreshToken)).ReturnsAsync(_session); + _jwtTokenHasherMock.Setup(h => h.HashToken(It.IsAny())).Returns(OldRefreshToken); // Act & Assert var ex = Assert.ThrowsAsync(async () => @@ -114,6 +122,7 @@ public class UserSessionRefresherTests // Arrange _session.ExpiresAt = DateTime.UtcNow.AddMinutes(-1); _sessionsRepoMock.Setup(r => r.GetByHashedRefreshTokenAsync(OldRefreshToken)).ReturnsAsync(_session); + _jwtTokenHasherMock.Setup(h => h.HashToken(It.IsAny())).Returns(OldRefreshToken); // Act & Assert var ex = Assert.ThrowsAsync(async () => @@ -128,6 +137,7 @@ public class UserSessionRefresherTests // Arrange _sessionsRepoMock.Setup(r => r.GetByHashedRefreshTokenAsync(OldRefreshToken)) .ThrowsAsync(new NotFoundByKeyException("token", OldRefreshToken)); + _jwtTokenHasherMock.Setup(h => h.HashToken(It.IsAny())).Returns(OldRefreshToken); // Act & Assert var ex = Assert.ThrowsAsync(async () => await _refresher.RefreshTokenAsync(OldRefreshToken)); diff --git a/Govor.Application/Services/UserSessions/UserSessionOpener.cs b/Govor.Application/Services/UserSessions/UserSessionOpener.cs index 7dda30f..bf4a382 100644 --- a/Govor.Application/Services/UserSessions/UserSessionOpener.cs +++ b/Govor.Application/Services/UserSessions/UserSessionOpener.cs @@ -38,14 +38,14 @@ public class UserSessionOpener : IUserSessionOpener try { var sessions = await _repository.GetByUserIdAsync(user.Id); - var existingSession = sessions.FirstOrDefault(s => s.DeviceInfo == deviceInfo); + var existingSession = sessions.FirstOrDefault(s => s.DeviceInfo == deviceInfo ); if (existingSession is not null) return await UpdateExistingSessionAsync(user, deviceInfo, existingSession); } catch (NotFoundByKeyException ex) { - + _logger.LogError(ex, "Could not find session for user {userId}", user.Id); } return await CreateNewSessionAsync(user, deviceInfo); diff --git a/Govor.Application/Services/UserSessions/UserSessionRefresher.cs b/Govor.Application/Services/UserSessions/UserSessionRefresher.cs index c737826..36823a2 100644 --- a/Govor.Application/Services/UserSessions/UserSessionRefresher.cs +++ b/Govor.Application/Services/UserSessions/UserSessionRefresher.cs @@ -39,14 +39,10 @@ public class UserSessionRefresher : IUserSessionRefresher { try { - var session = await _sessionsRepository.GetByHashedRefreshTokenAsync(_jwtTokenHasher.HashToken(refreshToken)); if (session.IsRevoked || session.ExpiresAt <= DateTime.UtcNow) throw new UnauthorizedAccessException("Refresh token is invalid or expired"); - - session.IsRevoked = true; - await _sessionsRepository.UpdateAsync(session); // Find user var user = await _usersRepository.FindByIdAsync(session.UserId); @@ -60,6 +56,7 @@ public class UserSessionRefresher : IUserSessionRefresher // Opening new session var newSession = new UserSession { + Id = session.Id, UserId = user.Id, RefreshTokenHash = newRefreshTokenHash, DeviceInfo = session.DeviceInfo, @@ -67,7 +64,7 @@ public class UserSessionRefresher : IUserSessionRefresher ExpiresAt = DateTime.UtcNow.AddDays(_options.RefreshTokenLifetimeDays) }; - await _sessionsRepository.AddAsync(newSession); + await _sessionsRepository.UpdateAsync(newSession); return new RefreshResult(newRefreshToken, newAccessToken); }