aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Timeline.Tests/Controllers/TokenControllerTest.cs35
-rw-r--r--Timeline.Tests/Controllers/UserControllerTest.cs21
-rw-r--r--Timeline.Tests/Helpers/MockUser.cs10
-rw-r--r--Timeline/Auth/MyAuthenticationHandler.cs10
-rw-r--r--Timeline/Controllers/TokenController.cs60
-rw-r--r--Timeline/Controllers/UserController.cs11
-rw-r--r--Timeline/Entities/UserAvatarEntity.cs4
-rw-r--r--Timeline/Entities/UserDetailEntity.cs4
-rw-r--r--Timeline/GlobalSuppressions.cs6
-rw-r--r--Timeline/Services/JwtBadVersionException.cs36
-rw-r--r--Timeline/Services/UserService.cs67
-rw-r--r--Timeline/Services/UsernameBadFormatException.cs11
-rw-r--r--Timeline/Startup.cs3
13 files changed, 142 insertions, 136 deletions
diff --git a/Timeline.Tests/Controllers/TokenControllerTest.cs b/Timeline.Tests/Controllers/TokenControllerTest.cs
index 740d8377..61fbe950 100644
--- a/Timeline.Tests/Controllers/TokenControllerTest.cs
+++ b/Timeline.Tests/Controllers/TokenControllerTest.cs
@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Timeline.Controllers;
+using Timeline.Models;
using Timeline.Models.Http;
using Timeline.Services;
using Timeline.Tests.Helpers;
@@ -15,7 +16,7 @@ namespace Timeline.Tests.Controllers
{
public class TokenControllerTest : IDisposable
{
- private readonly Mock<IUserService> _mockUserService = new Mock<IUserService>();
+ private readonly Mock<IUserTokenManager> _mockUserService = new Mock<IUserTokenManager>();
private readonly TestClock _mockClock = new TestClock();
@@ -38,12 +39,18 @@ namespace Timeline.Tests.Controllers
{
var mockCurrentTime = DateTime.Now;
_mockClock.MockCurrentTime = mockCurrentTime;
- var createResult = new CreateTokenResult
+ var mockCreateResult = new UserTokenCreateResult
{
Token = "mocktokenaaaaa",
- User = MockUser.User.Info
+ User = new UserInfo
+ {
+ Id = 1,
+ Username = MockUser.User.Username,
+ Administrator = MockUser.User.Administrator,
+ Version = 1
+ }
};
- _mockUserService.Setup(s => s.CreateToken("u", "p", expire == null ? null : (DateTime?)mockCurrentTime.AddDays(expire.Value))).ReturnsAsync(createResult);
+ _mockUserService.Setup(s => s.CreateToken("u", "p", expire == null ? null : (DateTime?)mockCurrentTime.AddDays(expire.Value))).ReturnsAsync(mockCreateResult);
var action = await _controller.Create(new CreateTokenRequest
{
Username = "u",
@@ -51,7 +58,11 @@ namespace Timeline.Tests.Controllers
Expire = expire
});
action.Result.Should().BeAssignableTo<OkObjectResult>()
- .Which.Value.Should().BeEquivalentTo(createResult);
+ .Which.Value.Should().BeEquivalentTo(new CreateTokenResponse
+ {
+ Token = mockCreateResult.Token,
+ User = MockUser.User.Info
+ });
}
[Fact]
@@ -88,7 +99,13 @@ namespace Timeline.Tests.Controllers
public async Task Verify_Ok()
{
const string token = "aaaaaaaaaaaaaa";
- _mockUserService.Setup(s => s.VerifyToken(token)).ReturnsAsync(MockUser.User.Info);
+ _mockUserService.Setup(s => s.VerifyToken(token)).ReturnsAsync(new UserInfo
+ {
+ Id = 1,
+ Username = MockUser.User.Username,
+ Administrator = MockUser.User.Administrator,
+ Version = 1
+ });
var action = await _controller.Verify(new VerifyTokenRequest { Token = token });
action.Result.Should().BeAssignableTo<OkObjectResult>()
.Which.Value.Should().BeAssignableTo<VerifyTokenResponse>()
@@ -97,9 +114,9 @@ namespace Timeline.Tests.Controllers
public static IEnumerable<object[]> Verify_BadRequest_Data()
{
- yield return new object[] { new JwtUserTokenBadFormatException(JwtUserTokenBadFormatException.ErrorCodes.Expired), ErrorCodes.TokenController.Verify_TimeExpired };
- yield return new object[] { new JwtUserTokenBadFormatException(JwtUserTokenBadFormatException.ErrorCodes.IdClaimBadFormat), ErrorCodes.TokenController.Verify_BadFormat };
- yield return new object[] { new JwtUserTokenBadFormatException(JwtUserTokenBadFormatException.ErrorCodes.OldVersion), ErrorCodes.TokenController.Verify_OldVersion };
+ yield return new object[] { new UserTokenTimeExpireException(), ErrorCodes.TokenController.Verify_TimeExpired };
+ yield return new object[] { new UserTokenBadVersionException(), ErrorCodes.TokenController.Verify_OldVersion };
+ yield return new object[] { new UserTokenBadFormatException(), ErrorCodes.TokenController.Verify_BadFormat };
yield return new object[] { new UserNotExistException(), ErrorCodes.TokenController.Verify_UserNotExist };
}
diff --git a/Timeline.Tests/Controllers/UserControllerTest.cs b/Timeline.Tests/Controllers/UserControllerTest.cs
index 262dbe11..a1035675 100644
--- a/Timeline.Tests/Controllers/UserControllerTest.cs
+++ b/Timeline.Tests/Controllers/UserControllerTest.cs
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
@@ -35,18 +36,28 @@ namespace Timeline.Tests.Controllers
[Fact]
public async Task GetList_Success()
{
- var array = MockUser.UserInfoList.ToArray();
- _mockUserService.Setup(s => s.ListUsers()).ReturnsAsync(array);
+ var mockUserList = new UserInfo[] {
+ new UserInfo { Id = 1, Username = "aaa", Administrator = true, Version = 1 },
+ new UserInfo { Id = 2, Username = "bbb", Administrator = false, Version = 1 }
+ };
+ _mockUserService.Setup(s => s.ListUsers()).ReturnsAsync(mockUserList);
var action = await _controller.List();
action.Result.Should().BeAssignableTo<OkObjectResult>()
- .Which.Value.Should().BeEquivalentTo(array);
+ .Which.Value.Should().BeEquivalentTo(
+ mockUserList.Select(u => new User { Username = u.Username, Administrator = u.Administrator }).ToArray());
}
[Fact]
public async Task Get_Success()
{
const string username = "aaa";
- _mockUserService.Setup(s => s.GetUserByUsername(username)).ReturnsAsync(MockUser.User.Info);
+ _mockUserService.Setup(s => s.GetUserByUsername(username)).ReturnsAsync(new UserInfo
+ {
+ Id = 1,
+ Username = MockUser.User.Username,
+ Administrator = MockUser.User.Administrator,
+ Version = 1
+ });
var action = await _controller.Get(username);
action.Result.Should().BeAssignableTo<OkObjectResult>()
.Which.Value.Should().BeEquivalentTo(MockUser.User.Info);
@@ -56,7 +67,7 @@ namespace Timeline.Tests.Controllers
public async Task Get_NotFound()
{
const string username = "aaa";
- _mockUserService.Setup(s => s.GetUserByUsername(username)).Returns(Task.FromResult<User>(null));
+ _mockUserService.Setup(s => s.GetUserByUsername(username)).ThrowsAsync(new UserNotExistException());
var action = await _controller.Get(username);
action.Result.Should().BeAssignableTo<NotFoundObjectResult>()
.Which.Value.Should().BeAssignableTo<CommonResponse>()
diff --git a/Timeline.Tests/Helpers/MockUser.cs b/Timeline.Tests/Helpers/MockUser.cs
index 8d738525..49576842 100644
--- a/Timeline.Tests/Helpers/MockUser.cs
+++ b/Timeline.Tests/Helpers/MockUser.cs
@@ -1,17 +1,17 @@
using System.Collections.Generic;
-using Timeline.Models;
-
+using Timeline.Models.Http;
+
namespace Timeline.Tests.Helpers
{
public class MockUser
{
public MockUser(string username, string password, bool administrator)
{
- Info = new UserInfo(username, administrator);
+ Info = new User { Username = username, Administrator = administrator };
Password = password;
}
- public UserInfo Info { get; set; }
+ public User Info { get; set; }
public string Username => Info.Username;
public string Password { get; set; }
public bool Administrator => Info.Administrator;
@@ -19,6 +19,6 @@ namespace Timeline.Tests.Helpers
public static MockUser User { get; } = new MockUser("user", "userpassword", false);
public static MockUser Admin { get; } = new MockUser("admin", "adminpassword", true);
- public static IReadOnlyList<UserInfo> UserInfoList { get; } = new List<UserInfo> { User.Info, Admin.Info };
+ public static IReadOnlyList<User> UserInfoList { get; } = new List<User> { User.Info, Admin.Info };
}
}
diff --git a/Timeline/Auth/MyAuthenticationHandler.cs b/Timeline/Auth/MyAuthenticationHandler.cs
index f5dcd697..5bae5117 100644
--- a/Timeline/Auth/MyAuthenticationHandler.cs
+++ b/Timeline/Auth/MyAuthenticationHandler.cs
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using System;
+using System.Globalization;
using System.Linq;
using System.Security.Claims;
using System.Text.Encodings.Web;
@@ -30,13 +31,13 @@ namespace Timeline.Auth
public class MyAuthenticationHandler : AuthenticationHandler<MyAuthenticationOptions>
{
private readonly ILogger<MyAuthenticationHandler> _logger;
- private readonly IUserService _userService;
+ private readonly IUserTokenManager _userTokenManager;
- public MyAuthenticationHandler(IOptionsMonitor<MyAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IUserService userService)
+ public MyAuthenticationHandler(IOptionsMonitor<MyAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IUserTokenManager userTokenManager)
: base(options, logger, encoder, clock)
{
_logger = logger.CreateLogger<MyAuthenticationHandler>();
- _userService = userService;
+ _userTokenManager = userTokenManager;
}
// return null if no token is found
@@ -78,9 +79,10 @@ namespace Timeline.Auth
try
{
- var userInfo = await _userService.VerifyToken(token);
+ var userInfo = await _userTokenManager.VerifyToken(token);
var identity = new ClaimsIdentity(AuthenticationConstants.Scheme);
+ identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userInfo.Id.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer64));
identity.AddClaim(new Claim(identity.NameClaimType, userInfo.Username, ClaimValueTypes.String));
identity.AddClaims(UserRoleConvert.ToArray(userInfo.Administrator).Select(role => new Claim(identity.RoleClaimType, role, ClaimValueTypes.String)));
diff --git a/Timeline/Controllers/TokenController.cs b/Timeline/Controllers/TokenController.cs
index 851c7606..a96b6fa9 100644
--- a/Timeline/Controllers/TokenController.cs
+++ b/Timeline/Controllers/TokenController.cs
@@ -1,11 +1,11 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
-using Microsoft.IdentityModel.Tokens;
using System;
using System.Globalization;
using System.Threading.Tasks;
using Timeline.Helpers;
+using Timeline.Models;
using Timeline.Models.Http;
using Timeline.Services;
using static Timeline.Resources.Controllers.TokenController;
@@ -16,13 +16,22 @@ namespace Timeline.Controllers
[ApiController]
public class TokenController : Controller
{
- private readonly IUserService _userService;
+ private readonly IUserTokenManager _userTokenManager;
private readonly ILogger<TokenController> _logger;
private readonly IClock _clock;
- public TokenController(IUserService userService, ILogger<TokenController> logger, IClock clock)
+ private static User CreateUserFromUserInfo(UserInfo userInfo)
{
- _userService = userService;
+ return new User
+ {
+ Username = userInfo.Username,
+ Administrator = userInfo.Administrator
+ };
+ }
+
+ public TokenController(IUserTokenManager userTokenManager, ILogger<TokenController> logger, IClock clock)
+ {
+ _userTokenManager = userTokenManager;
_logger = logger;
_clock = clock;
}
@@ -47,7 +56,7 @@ namespace Timeline.Controllers
if (request.Expire != null)
expireTime = _clock.GetCurrentTime().AddDays(request.Expire.Value);
- var result = await _userService.CreateToken(request.Username, request.Password, expireTime);
+ var result = await _userTokenManager.CreateToken(request.Username, request.Password, expireTime);
_logger.LogInformation(Log.Format(LogCreateSuccess,
("Username", request.Username),
@@ -56,7 +65,7 @@ namespace Timeline.Controllers
return Ok(new CreateTokenResponse
{
Token = result.Token,
- User = result.User
+ User = CreateUserFromUserInfo(result.User)
});
}
catch (UserNotExistException e)
@@ -86,36 +95,29 @@ namespace Timeline.Controllers
try
{
- var result = await _userService.VerifyToken(request.Token);
+ var result = await _userTokenManager.VerifyToken(request.Token);
_logger.LogInformation(Log.Format(LogVerifySuccess,
("Username", result.Username), ("Token", request.Token)));
return Ok(new VerifyTokenResponse
{
- User = result
+ User = CreateUserFromUserInfo(result)
});
}
- catch (JwtUserTokenBadFormatException e)
+ catch (UserTokenTimeExpireException e)
{
- if (e.ErrorCode == JwtUserTokenBadFormatException.ErrorCodes.Expired)
- {
- var innerException = e.InnerException as SecurityTokenExpiredException;
- LogFailure(LogVerifyExpire, e, ("Expires", innerException?.Expires),
- ("Current Time", _clock.GetCurrentTime()));
- return BadRequest(ErrorResponse.TokenController.Verify_TimeExpired());
- }
- else if (e.ErrorCode == JwtUserTokenBadFormatException.ErrorCodes.OldVersion)
- {
- var innerException = e.InnerException as JwtBadVersionException;
- LogFailure(LogVerifyOldVersion, e,
- ("Token Version", innerException?.TokenVersion),
- ("Required Version", innerException?.RequiredVersion));
- return BadRequest(ErrorResponse.TokenController.Verify_OldVersion());
- }
- else
- {
- LogFailure(LogVerifyBadFormat, e);
- return BadRequest(ErrorResponse.TokenController.Verify_BadFormat());
- }
+ LogFailure(LogVerifyExpire, e, ("Expire Time", e.ExpireTime), ("Verify Time", e.VerifyTime));
+ return BadRequest(ErrorResponse.TokenController.Verify_TimeExpired());
+ }
+ catch (UserTokenBadVersionException e)
+ {
+ LogFailure(LogVerifyOldVersion, e, ("Token Version", e.TokenVersion), ("Required Version", e.RequiredVersion));
+ return BadRequest(ErrorResponse.TokenController.Verify_OldVersion());
+
+ }
+ catch (UserTokenBadFormatException e)
+ {
+ LogFailure(LogVerifyBadFormat, e);
+ return BadRequest(ErrorResponse.TokenController.Verify_BadFormat());
}
catch (UserNotExistException e)
{
diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs
index 65ee3a0f..5f1b7bd7 100644
--- a/Timeline/Controllers/UserController.cs
+++ b/Timeline/Controllers/UserController.cs
@@ -35,13 +35,16 @@ namespace Timeline.Controllers
[HttpGet("users/{username}"), AdminAuthorize]
public async Task<ActionResult<User>> Get([FromRoute][Username] string username)
{
- var user = await _userService.GetUserByUsername(username);
- if (user == null)
+ try
+ {
+ var user = await _userService.GetUserByUsername(username);
+ return Ok(user);
+ }
+ catch (UserNotExistException e)
{
- _logger.LogInformation(Log.Format(LogGetUserNotExist, ("Username", username)));
+ _logger.LogInformation(e, Log.Format(LogGetUserNotExist, ("Username", username)));
return NotFound(ErrorResponse.UserCommon.NotExist());
}
- return Ok(user);
}
[HttpPut("users/{username}"), AdminAuthorize]
diff --git a/Timeline/Entities/UserAvatarEntity.cs b/Timeline/Entities/UserAvatarEntity.cs
index eed819bc..6cecce1a 100644
--- a/Timeline/Entities/UserAvatarEntity.cs
+++ b/Timeline/Entities/UserAvatarEntity.cs
@@ -23,6 +23,10 @@ namespace Timeline.Entities
[Column("last_modified"), Required]
public DateTime LastModified { get; set; }
+ [Column("user"), Required]
public long UserId { get; set; }
+
+ [ForeignKey(nameof(UserId))]
+ public UserEntity User { get; set; } = default!;
}
}
diff --git a/Timeline/Entities/UserDetailEntity.cs b/Timeline/Entities/UserDetailEntity.cs
index 7a525294..1d9957f9 100644
--- a/Timeline/Entities/UserDetailEntity.cs
+++ b/Timeline/Entities/UserDetailEntity.cs
@@ -12,6 +12,10 @@ namespace Timeline.Entities
[Column("nickname"), MaxLength(26)]
public string? Nickname { get; set; }
+ [Column("user")]
public long UserId { get; set; }
+
+ [ForeignKey(nameof(UserId))]
+ public UserEntity User { get; set; } = default!;
}
}
diff --git a/Timeline/GlobalSuppressions.cs b/Timeline/GlobalSuppressions.cs
index 076c2005..c0754071 100644
--- a/Timeline/GlobalSuppressions.cs
+++ b/Timeline/GlobalSuppressions.cs
@@ -7,6 +7,6 @@
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "This is not bad.")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "No need to check the null because it's ASP.Net's duty.", Scope = "namespaceanddescendants", Target = "Timeline.Controllers")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Migrations code are auto generated.", Scope = "namespaceanddescendants", Target = "Timeline.Migrations")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Error code constant identifiers.", Scope = "type", Target = "Timeline.ErrorCodes")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1716:Identifiers should not match keywords", Justification = "Error code constant identifiers.", Scope = "type", Target = "Timeline.ErrorCodes")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1724:Type names should not match namespaces", Justification = "Error code constant identifiers.", Scope = "type", Target = "Timeline.ErrorCodes")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Generated error response identifiers.", Scope = "type", Target = "Timeline.Models.Http.ErrorResponse")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1724:Type names should not match namespaces", Justification = "Generated error response identifiers.", Scope = "type", Target = "Timeline.Models.Http.ErrorResponse")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "Generated error response.", Scope = "type", Target = "Timeline.Models.Http.ErrorResponse")]
diff --git a/Timeline/Services/JwtBadVersionException.cs b/Timeline/Services/JwtBadVersionException.cs
deleted file mode 100644
index 4ce17710..00000000
--- a/Timeline/Services/JwtBadVersionException.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using Timeline.Helpers;
-
-namespace Timeline.Services
-{
- [Serializable]
- public class JwtBadVersionException : Exception
- {
- public JwtBadVersionException() : base(Resources.Services.Exception.JwtBadVersionException) { }
- public JwtBadVersionException(string message) : base(message) { }
- public JwtBadVersionException(string message, Exception inner) : base(message, inner) { }
-
- public JwtBadVersionException(long tokenVersion, long requiredVersion)
- : base(Log.Format(Resources.Services.Exception.JwtBadVersionException,
- ("Token Version", tokenVersion),
- ("Required Version", requiredVersion)))
- {
- TokenVersion = tokenVersion;
- RequiredVersion = requiredVersion;
- }
-
- protected JwtBadVersionException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- /// <summary>
- /// The version in the token.
- /// </summary>
- public long? TokenVersion { get; set; }
-
- /// <summary>
- /// The version required.
- /// </summary>
- public long? RequiredVersion { get; set; }
- }
-}
diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs
index db2350a2..104db1b0 100644
--- a/Timeline/Services/UserService.cs
+++ b/Timeline/Services/UserService.cs
@@ -139,19 +139,30 @@ namespace Timeline.Services
_logger.LogInformation(Log.Format(Resources.Services.UserService.LogCacheRemove, ("Key", key)));
}
- private void CheckUsernameFormat(string username, string? additionalMessage = null)
+ private void CheckUsernameFormat(string username, string? message = null)
{
- var (result, message) = _usernameValidator.Validate(username);
+ var (result, validationMessage) = _usernameValidator.Validate(username);
if (!result)
{
- if (additionalMessage == null)
- throw new UsernameBadFormatException(username, message);
+ if (message == null)
+ throw new UsernameBadFormatException(username, validationMessage);
else
- throw new UsernameBadFormatException(username, additionalMessage + message);
+ throw new UsernameBadFormatException(username, validationMessage, message);
}
}
- public async Task<UserInfo> CheckCredential(string username, string password)
+ private static UserInfo CreateUserInfoFromEntity(UserEntity user)
+ {
+ return new UserInfo
+ {
+ Id = user.Id,
+ Username = user.Name,
+ Administrator = UserRoleConvert.ToBool(user.RoleString),
+ Version = user.Version
+ };
+ }
+
+ public async Task<UserInfo> VerifyCredential(string username, string password)
{
if (username == null)
throw new ArgumentNullException(nameof(username));
@@ -169,30 +180,13 @@ namespace Timeline.Services
if (!_passwordService.VerifyPassword(user.EncryptedPassword, password))
throw new BadPasswordException(password);
- var token = _jwtService.GenerateJwtToken(new TokenInfo
- {
- Id = user.Id,
- Version = user.Version
- }, expires);
-
- return new CreateTokenResult
- {
- Token = token,
- User = UserConvert.CreateUserInfo(user)
- };
+ return CreateUserInfoFromEntity(user);
}
- public async Task<UserInfo> VerifyToken(string token)
+ public async Task<UserInfo> GetUserById(long id)
{
- if (token == null)
- throw new ArgumentNullException(nameof(token));
-
- TokenInfo tokenInfo;
- tokenInfo = _jwtService.VerifyJwtToken(token);
-
- var id = tokenInfo.Id;
var key = GenerateCacheKeyByUserId(id);
- if (!_memoryCache.TryGetValue<UserCache>(key, out var cache))
+ if (!_memoryCache.TryGetValue<UserInfo>(key, out var cache))
{
// no cache, check the database
var user = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync();
@@ -201,34 +195,35 @@ namespace Timeline.Services
throw new UserNotExistException(id);
// create cache
- cache = UserConvert.CreateUserCache(user);
+ cache = CreateUserInfoFromEntity(user);
_memoryCache.CreateEntry(key).SetValue(cache);
_logger.LogInformation(Log.Format(Resources.Services.UserService.LogCacheCreate, ("Key", key)));
}
- if (tokenInfo.Version != cache.Version)
- throw new JwtUserTokenBadFormatException(new JwtBadVersionException(tokenInfo.Version, cache.Version), JwtUserTokenBadFormatException.ErrorCodes.OldVersion);
-
- return cache.ToUserInfo();
+ return cache;
}
public async Task<UserInfo> GetUserByUsername(string username)
{
if (username == null)
throw new ArgumentNullException(nameof(username));
+
CheckUsernameFormat(username);
- return await _databaseContext.Users
+ var entity = await _databaseContext.Users
.Where(user => user.Name == username)
- .Select(user => UserConvert.CreateUserInfo(user))
.SingleOrDefaultAsync();
+
+ if (entity == null)
+ throw new UserNotExistException(username);
+
+ return CreateUserInfoFromEntity(entity);
}
public async Task<UserInfo[]> ListUsers()
{
- return await _databaseContext.Users
- .Select(user => UserConvert.CreateUserInfo(user))
- .ToArrayAsync();
+ var entities = await _databaseContext.Users.ToArrayAsync();
+ return entities.Select(user => CreateUserInfoFromEntity(user)).ToArray();
}
public async Task<PutResult> PutUser(string username, string password, bool administrator)
diff --git a/Timeline/Services/UsernameBadFormatException.cs b/Timeline/Services/UsernameBadFormatException.cs
index 991be7df..ad0350b5 100644
--- a/Timeline/Services/UsernameBadFormatException.cs
+++ b/Timeline/Services/UsernameBadFormatException.cs
@@ -9,11 +9,12 @@ namespace Timeline.Services
public class UsernameBadFormatException : Exception
{
public UsernameBadFormatException() : base(Resources.Services.Exception.UsernameBadFormatException) { }
- public UsernameBadFormatException(string username) : this() { Username = username; }
- public UsernameBadFormatException(string username, Exception inner) : base(Resources.Services.Exception.UsernameBadFormatException, inner) { Username = username; }
+ public UsernameBadFormatException(string message) : base(message) { }
+ public UsernameBadFormatException(string message, Exception inner) : base(message, inner) { }
- public UsernameBadFormatException(string username, string message) : base(message) { Username = username; }
- public UsernameBadFormatException(string username, string message, Exception inner) : base(message, inner) { Username = username; }
+ public UsernameBadFormatException(string username, string validationMessage) : this() { Username = username; ValidationMessage = validationMessage; }
+
+ public UsernameBadFormatException(string username, string validationMessage, string message) : this(message) { Username = username; ValidationMessage = validationMessage; }
protected UsernameBadFormatException(
System.Runtime.Serialization.SerializationInfo info,
@@ -23,5 +24,7 @@ namespace Timeline.Services
/// Username of bad format.
/// </summary>
public string Username { get; private set; } = "";
+
+ public string ValidationMessage { get; private set; } = "";
}
}
diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs
index c1e73182..379ce6ea 100644
--- a/Timeline/Startup.cs
+++ b/Timeline/Startup.cs
@@ -84,7 +84,8 @@ namespace Timeline
});
services.AddScoped<IUserService, UserService>();
- services.AddScoped<IJwtService, UserTokenService>();
+ services.AddScoped<IUserTokenService, JwtUserTokenService>();
+ services.AddScoped<IUserTokenManager, UserTokenManager>();
services.AddTransient<IPasswordService, PasswordService>();
services.AddTransient<IClock, Clock>();
services.AddUserAvatarService();