From c442b7ad597f430b186dd8019de70332b574c4ba Mon Sep 17 00:00:00 2001 From: 杨宇千 Date: Mon, 21 Oct 2019 20:47:31 +0800 Subject: ... --- Timeline/Services/BadPasswordException.cs | 27 ++++ Timeline/Services/JwtBadVersionException.cs | 36 +++++ Timeline/Services/JwtService.cs | 76 ++------- Timeline/Services/JwtVerifyException.cs | 59 +++++++ Timeline/Services/UserDetailService.cs | 135 ---------------- Timeline/Services/UserNotExistException.cs | 41 +++++ Timeline/Services/UserService.cs | 195 +++--------------------- Timeline/Services/UsernameBadFormatException.cs | 27 ++++ Timeline/Services/UsernameConfictException.cs | 25 +++ 9 files changed, 247 insertions(+), 374 deletions(-) create mode 100644 Timeline/Services/BadPasswordException.cs create mode 100644 Timeline/Services/JwtBadVersionException.cs create mode 100644 Timeline/Services/JwtVerifyException.cs delete mode 100644 Timeline/Services/UserDetailService.cs create mode 100644 Timeline/Services/UserNotExistException.cs create mode 100644 Timeline/Services/UsernameBadFormatException.cs create mode 100644 Timeline/Services/UsernameConfictException.cs (limited to 'Timeline/Services') diff --git a/Timeline/Services/BadPasswordException.cs b/Timeline/Services/BadPasswordException.cs new file mode 100644 index 00000000..ee8a42db --- /dev/null +++ b/Timeline/Services/BadPasswordException.cs @@ -0,0 +1,27 @@ +using System; +using Timeline.Helpers; + +namespace Timeline.Services +{ + [Serializable] + public class BadPasswordException : Exception + { + public BadPasswordException() : base(Resources.Services.Exception.UserNotExistException) { } + public BadPasswordException(string message, Exception inner) : base(message, inner) { } + + public BadPasswordException(string badPassword) + : base(Log.Format(Resources.Services.Exception.UserNotExistException, ("Bad Password", badPassword))) + { + Password = badPassword; + } + + protected BadPasswordException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + + /// + /// The wrong password. + /// + public string? Password { get; set; } + } +} diff --git a/Timeline/Services/JwtBadVersionException.cs b/Timeline/Services/JwtBadVersionException.cs new file mode 100644 index 00000000..4ce17710 --- /dev/null +++ b/Timeline/Services/JwtBadVersionException.cs @@ -0,0 +1,36 @@ +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) { } + + /// + /// The version in the token. + /// + public long? TokenVersion { get; set; } + + /// + /// The version required. + /// + public long? RequiredVersion { get; set; } + } +} diff --git a/Timeline/Services/JwtService.cs b/Timeline/Services/JwtService.cs index 90d0c217..bf92966a 100644 --- a/Timeline/Services/JwtService.cs +++ b/Timeline/Services/JwtService.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using System; +using System.Globalization; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; @@ -14,63 +15,6 @@ namespace Timeline.Services public long Version { get; set; } } - [Serializable] - public class JwtTokenVerifyException : Exception - { - public static class ErrorCodes - { - // Codes in -1000 ~ -1999 usually means the user provides a token that is not created by this server. - - public const int Others = -1001; - public const int NoIdClaim = -1002; - public const int IdClaimBadFormat = -1003; - public const int NoVersionClaim = -1004; - public const int VersionClaimBadFormat = -1005; - - /// - /// Corresponds to . - /// - public const int Expired = -2001; - } - - private const string message = "Jwt token is bad."; - - public JwtTokenVerifyException() : base(message) { } - public JwtTokenVerifyException(string message) : base(message) { } - public JwtTokenVerifyException(string message, Exception inner) : base(message, inner) { } - - public JwtTokenVerifyException(int code) : base(GetErrorMessage(code)) { ErrorCode = code; } - public JwtTokenVerifyException(string message, int code) : base(message) { ErrorCode = code; } - public JwtTokenVerifyException(Exception inner, int code) : base(GetErrorMessage(code), inner) { ErrorCode = code; } - public JwtTokenVerifyException(string message, Exception inner, int code) : base(message, inner) { ErrorCode = code; } - protected JwtTokenVerifyException( - System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) : base(info, context) { } - - public int ErrorCode { get; set; } - - private static string GetErrorMessage(int errorCode) - { - switch (errorCode) - { - case ErrorCodes.Others: - return "Uncommon error, see inner exception for more information."; - case ErrorCodes.NoIdClaim: - return "Id claim does not exist."; - case ErrorCodes.IdClaimBadFormat: - return "Id claim is not a number."; - case ErrorCodes.NoVersionClaim: - return "Version claim does not exist."; - case ErrorCodes.VersionClaimBadFormat: - return "Version claim is not a number"; - case ErrorCodes.Expired: - return "Token is expired."; - default: - return "Unknown error code."; - } - } - } - public interface IJwtService { /// @@ -89,7 +33,7 @@ namespace Timeline.Services /// The token string to verify. /// Return the saved info in token. /// Thrown when is null. - /// Thrown when the token is invalid. + /// Thrown when the token is invalid. TokenInfo VerifyJwtToken(string token); } @@ -116,8 +60,8 @@ namespace Timeline.Services var config = _jwtConfig.CurrentValue; var identity = new ClaimsIdentity(); - identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, tokenInfo.Id.ToString(), ClaimValueTypes.Integer64)); - identity.AddClaim(new Claim(VersionClaimType, tokenInfo.Version.ToString(), ClaimValueTypes.Integer64)); + identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, tokenInfo.Id.ToString(CultureInfo.InvariantCulture.NumberFormat), ClaimValueTypes.Integer64)); + identity.AddClaim(new Claim(VersionClaimType, tokenInfo.Version.ToString(CultureInfo.InvariantCulture.NumberFormat), ClaimValueTypes.Integer64)); var tokenDescriptor = new SecurityTokenDescriptor() { @@ -159,15 +103,15 @@ namespace Timeline.Services var idClaim = principal.FindFirstValue(ClaimTypes.NameIdentifier); if (idClaim == null) - throw new JwtTokenVerifyException(JwtTokenVerifyException.ErrorCodes.NoIdClaim); + throw new JwtVerifyException(JwtVerifyException.ErrorCodes.NoIdClaim); if (!long.TryParse(idClaim, out var id)) - throw new JwtTokenVerifyException(JwtTokenVerifyException.ErrorCodes.IdClaimBadFormat); + throw new JwtVerifyException(JwtVerifyException.ErrorCodes.IdClaimBadFormat); var versionClaim = principal.FindFirstValue(VersionClaimType); if (versionClaim == null) - throw new JwtTokenVerifyException(JwtTokenVerifyException.ErrorCodes.NoVersionClaim); + throw new JwtVerifyException(JwtVerifyException.ErrorCodes.NoVersionClaim); if (!long.TryParse(versionClaim, out var version)) - throw new JwtTokenVerifyException(JwtTokenVerifyException.ErrorCodes.VersionClaimBadFormat); + throw new JwtVerifyException(JwtVerifyException.ErrorCodes.VersionClaimBadFormat); return new TokenInfo { @@ -177,11 +121,11 @@ namespace Timeline.Services } catch (SecurityTokenExpiredException e) { - throw new JwtTokenVerifyException(e, JwtTokenVerifyException.ErrorCodes.Expired); + throw new JwtVerifyException(e, JwtVerifyException.ErrorCodes.Expired); } catch (Exception e) { - throw new JwtTokenVerifyException(e, JwtTokenVerifyException.ErrorCodes.Others); + throw new JwtVerifyException(e, JwtVerifyException.ErrorCodes.Others); } } } diff --git a/Timeline/Services/JwtVerifyException.cs b/Timeline/Services/JwtVerifyException.cs new file mode 100644 index 00000000..a915b51a --- /dev/null +++ b/Timeline/Services/JwtVerifyException.cs @@ -0,0 +1,59 @@ +using Microsoft.IdentityModel.Tokens; +using System; +using System.Globalization; +using static Timeline.Resources.Services.Exception; + +namespace Timeline.Services +{ + [Serializable] + public class JwtVerifyException : Exception + { + public static class ErrorCodes + { + // Codes in -1000 ~ -1999 usually means the user provides a token that is not created by this server. + + public const int Others = -1001; + public const int NoIdClaim = -1002; + public const int IdClaimBadFormat = -1003; + public const int NoVersionClaim = -1004; + public const int VersionClaimBadFormat = -1005; + + /// + /// Corresponds to . + /// + public const int Expired = -2001; + public const int OldVersion = -2002; + } + + public JwtVerifyException() : base(GetErrorMessage(0)) { } + public JwtVerifyException(string message) : base(message) { } + public JwtVerifyException(string message, Exception inner) : base(message, inner) { } + + public JwtVerifyException(int code) : base(GetErrorMessage(code)) { ErrorCode = code; } + public JwtVerifyException(string message, int code) : base(message) { ErrorCode = code; } + public JwtVerifyException(Exception inner, int code) : base(GetErrorMessage(code), inner) { ErrorCode = code; } + public JwtVerifyException(string message, Exception inner, int code) : base(message, inner) { ErrorCode = code; } + protected JwtVerifyException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + + public int ErrorCode { get; set; } + + private static string GetErrorMessage(int errorCode) + { + var reason = errorCode switch + { + ErrorCodes.Others => JwtVerifyExceptionOthers, + ErrorCodes.NoIdClaim => JwtVerifyExceptionNoIdClaim, + ErrorCodes.IdClaimBadFormat => JwtVerifyExceptionIdClaimBadFormat, + ErrorCodes.NoVersionClaim => JwtVerifyExceptionNoVersionClaim, + ErrorCodes.VersionClaimBadFormat => JwtVerifyExceptionVersionClaimBadFormat, + ErrorCodes.Expired => JwtVerifyExceptionExpired, + ErrorCodes.OldVersion => JwtVerifyExceptionOldVersion, + _ => JwtVerifyExceptionUnknown + }; + + return string.Format(CultureInfo.InvariantCulture, Resources.Services.Exception.JwtVerifyException, reason); + } + } +} diff --git a/Timeline/Services/UserDetailService.cs b/Timeline/Services/UserDetailService.cs deleted file mode 100644 index 5e049435..00000000 --- a/Timeline/Services/UserDetailService.cs +++ /dev/null @@ -1,135 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System; -using System.Linq; -using System.Threading.Tasks; -using Timeline.Entities; -using Timeline.Models; - -namespace Timeline.Services -{ - public interface IUserDetailService - { - /// - /// Get the nickname of user. - /// - /// The username to get nickname of. - /// The user's nickname. Null if not set. - /// Thrown if is null or empty. - /// Thrown if user doesn't exist. - Task GetUserNickname(string username); - - /// - /// Get the detail of user. - /// - /// The username to get user detail of. - /// The user detail. - /// Thrown if is null or empty. - /// Thrown if user doesn't exist. - Task GetUserDetail(string username); - - /// - /// Update the detail of user. This function does not do data check. - /// - /// The username to get user detail of. - /// The detail to update. Can't be null. Any null member means not set. - /// Thrown if is null or empty or is null. - /// Thrown if user doesn't exist. - Task UpdateUserDetail(string username, UserDetail detail); - } - - public class UserDetailService : IUserDetailService - { - private readonly ILogger _logger; - - private readonly DatabaseContext _databaseContext; - - public UserDetailService(ILogger logger, DatabaseContext databaseContext) - { - _logger = logger; - _databaseContext = databaseContext; - } - - private async Task CreateEntity(long userId) - { - var entity = new UserDetailEntity() - { - UserId = userId - }; - _databaseContext.UserDetails.Add(entity); - await _databaseContext.SaveChangesAsync(); - _logger.LogInformation("An entity is created in user_details."); - return entity; - } - - // Check the existence of user detail entry - private async Task CheckAndInit(long userId) - { - var detail = await _databaseContext.UserDetails.Where(e => e.UserId == userId).SingleOrDefaultAsync(); - if (detail == null) - { - detail = await CreateEntity(userId); - } - return detail; - } - - public async Task GetUserNickname(string username) - { - var userId = await DatabaseExtensions.CheckAndGetUser(_databaseContext.Users, username); - var detail = await _databaseContext.UserDetails.Where(e => e.UserId == userId).Select(e => new { e.Nickname }).SingleOrDefaultAsync(); - if (detail == null) - { - var entity = await CreateEntity(userId); - return null; - } - else - { - var nickname = detail.Nickname; - return string.IsNullOrEmpty(nickname) ? null : nickname; - } - } - - public async Task GetUserDetail(string username) - { - var userId = await DatabaseExtensions.CheckAndGetUser(_databaseContext.Users, username); - var detailEntity = await CheckAndInit(userId); - return UserDetail.From(detailEntity); - } - - public async Task UpdateUserDetail(string username, UserDetail detail) - { - if (detail == null) - throw new ArgumentNullException(nameof(detail)); - - var userId = await DatabaseExtensions.CheckAndGetUser(_databaseContext.Users, username); - var detailEntity = await CheckAndInit(userId); - - if (detail.Nickname != null) - detailEntity.Nickname = detail.Nickname; - - if (detail.QQ != null) - detailEntity.QQ = detail.QQ; - - if (detail.Email != null) - detailEntity.Email = detail.Email; - - if (detail.PhoneNumber != null) - detailEntity.PhoneNumber = detail.PhoneNumber; - - if (detail.Description != null) - detailEntity.Description = detail.Description; - - await _databaseContext.SaveChangesAsync(); - _logger.LogInformation("An entity is updated in user_details."); - } - } - - public static class UserDetailServiceCollectionExtensions - { - public static void AddUserDetailService(this IServiceCollection services) - { - services.AddScoped(); - } - } -} diff --git a/Timeline/Services/UserNotExistException.cs b/Timeline/Services/UserNotExistException.cs new file mode 100644 index 00000000..c7317f56 --- /dev/null +++ b/Timeline/Services/UserNotExistException.cs @@ -0,0 +1,41 @@ +using System; +using Timeline.Helpers; + +namespace Timeline.Services +{ + /// + /// The user requested does not exist. + /// + [Serializable] + public class UserNotExistException : Exception + { + public UserNotExistException() : base(Resources.Services.Exception.UserNotExistException) { } + public UserNotExistException(string message, Exception inner) : base(message, inner) { } + + public UserNotExistException(string username) + : base(Log.Format(Resources.Services.Exception.UserNotExistException, ("Username", username))) + { + Username = username; + } + + public UserNotExistException(long id) + : base(Log.Format(Resources.Services.Exception.UserNotExistException, ("Id", id))) + { + Id = id; + } + + protected UserNotExistException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + + /// + /// The username of the user that does not exist. + /// + public string? Username { get; set; } + + /// + /// The id of the user that does not exist. + /// + public long? Id { get; set; } + } +} diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs index 9564b34b..aad4a806 100644 --- a/Timeline/Services/UserService.cs +++ b/Timeline/Services/UserService.cs @@ -1,11 +1,11 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using System; using System.Linq; using System.Threading.Tasks; using Timeline.Entities; -using Timeline.Helpers; using Timeline.Models; using Timeline.Models.Validation; using static Timeline.Helpers.MyLogHelper; @@ -15,163 +15,8 @@ namespace Timeline.Services { public class CreateTokenResult { - public string Token { get; set; } - public UserInfo User { get; set; } - } - - [Serializable] - public class UserNotExistException : Exception - { - private const string message = "The user does not exist."; - - public UserNotExistException() - : base(message) - { - - } - - public UserNotExistException(string username) - : base(Log.Format(message, ("Username", username))) - { - Username = username; - } - - public UserNotExistException(long id) - : base(Log.Format(message, ("Id", id))) - { - Id = id; - } - - public UserNotExistException(string message, Exception inner) : base(message, inner) { } - - protected UserNotExistException( - System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) : base(info, context) { } - - /// - /// The username that does not exist. - /// - public string Username { get; set; } - - /// - /// The id that does not exist. - /// - public long? Id { get; set; } - } - - [Serializable] - public class BadPasswordException : Exception - { - private const string message = "Password is wrong."; - - public BadPasswordException() - : base(message) - { - - } - - public BadPasswordException(string badPassword) - : base(Log.Format(message, ("Bad Password", badPassword))) - { - Password = badPassword; - } - - public BadPasswordException(string message, Exception inner) : base(message, inner) { } - - protected BadPasswordException( - System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) : base(info, context) { } - - /// - /// The wrong password. - /// - public string Password { get; set; } - } - - - [Serializable] - public class BadTokenVersionException : Exception - { - private const string message = "Token version is expired."; - - public BadTokenVersionException() - : base(message) - { - - } - - public BadTokenVersionException(long tokenVersion, long requiredVersion) - : base(Log.Format(message, - ("Token Version", tokenVersion), - ("Required Version", requiredVersion))) - { - TokenVersion = tokenVersion; - RequiredVersion = requiredVersion; - } - - public BadTokenVersionException(string message) : base(message) { } - public BadTokenVersionException(string message, Exception inner) : base(message, inner) { } - - protected BadTokenVersionException( - System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) : base(info, context) { } - - /// - /// The version in the token. - /// - public long? TokenVersion { get; set; } - - /// - /// The version required. - /// - public long? RequiredVersion { get; set; } - } - - /// - /// Thrown when username is of bad format. - /// - [Serializable] - public class UsernameBadFormatException : Exception - { - private const string message = "Username is of bad format."; - - public UsernameBadFormatException() : base(message) { } - 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; } - protected UsernameBadFormatException( - System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) : base(info, context) { } - - /// - /// Username of bad format. - /// - public string Username { get; private set; } - } - - - /// - /// Thrown when the user already exists. - /// - [Serializable] - public class UserAlreadyExistException : Exception - { - private const string message = "User already exists."; - - public UserAlreadyExistException() : base(message) { } - public UserAlreadyExistException(string username) : base(Log.Format(message, ("Username", username))) { Username = username; } - public UserAlreadyExistException(string username, string message) : base(message) { Username = username; } - public UserAlreadyExistException(string message, Exception inner) : base(message, inner) { } - protected UserAlreadyExistException( - System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) : base(info, context) { } - - /// - /// The username that already exists. - /// - public string Username { get; set; } + public string Token { get; set; } = default!; + public UserInfo User { get; set; } = default!; } public interface IUserService @@ -196,9 +41,8 @@ namespace Timeline.Services /// The token to verify. /// The user info specified by the token. /// Thrown when is null. - /// Thrown when the token is of bad format. Thrown by . + /// Thrown when the token is of bad format. Thrown by . /// Thrown when the user specified by the token does not exist. Usually it has been deleted after the token was issued. - /// Thrown when the version in the token is expired. User needs to recreate the token. Task VerifyToken(string token); /// @@ -221,10 +65,12 @@ namespace Timeline.Services /// Username of user. /// Password of user. /// Whether the user is administrator. - /// Return if a new user is created. - /// Return if a existing user is modified. - /// Thrown when is of bad format. + /// + /// Return if a new user is created. + /// Return if a existing user is modified. + /// /// Thrown when or is null. + /// Thrown when is of bad format. Task PutUser(string username, string password, bool administrator); /// @@ -237,7 +83,7 @@ namespace Timeline.Services /// Whether the user is administrator. Null if not modify. /// Thrown if is null. /// Thrown if the user with given username does not exist. - Task PatchUser(string username, string password, bool? administrator); + Task PatchUser(string username, string? password, bool? administrator); /// /// Delete a user of given username. @@ -266,13 +112,13 @@ namespace Timeline.Services /// Thrown if or is null or empty. /// Thrown if the user with old username does not exist. /// Thrown if the new username is not accepted because of bad format. - /// Thrown if user with the new username already exists. + /// Thrown if user with the new username already exists. Task ChangeUsername(string oldUsername, string newUsername); } internal class UserCache { - public string Username { get; set; } + public string Username { get; set; } = default!; public bool Administrator { get; set; } public long Version { get; set; } @@ -294,13 +140,16 @@ namespace Timeline.Services private readonly UsernameValidator _usernameValidator; - public UserService(ILogger logger, IMemoryCache memoryCache, DatabaseContext databaseContext, IJwtService jwtService, IPasswordService passwordService) + private readonly IStringLocalizerFactory _localizerFactory; + + public UserService(ILogger logger, IMemoryCache memoryCache, IStringLocalizerFactory localizerFactory, DatabaseContext databaseContext, IJwtService jwtService, IPasswordService passwordService) { _logger = logger; _memoryCache = memoryCache; _databaseContext = databaseContext; _jwtService = jwtService; _passwordService = passwordService; + _localizerFactory = localizerFactory; _usernameValidator = new UsernameValidator(); } @@ -368,7 +217,7 @@ namespace Timeline.Services } if (tokenInfo.Version != cache.Version) - throw new BadTokenVersionException(tokenInfo.Version, cache.Version); + throw new JwtVerifyException(new JwtBadVersionException(tokenInfo.Version, cache.Version), JwtVerifyException.ErrorCodes.OldVersion); return cache.ToUserInfo(); } @@ -395,7 +244,7 @@ namespace Timeline.Services if (password == null) throw new ArgumentNullException(nameof(password)); - if (!_usernameValidator.Validate(username, out var message)) + if (!_usernameValidator.Validate(username, _localizerFactory, out var message)) { throw new UsernameBadFormatException(username, message); } @@ -414,7 +263,7 @@ namespace Timeline.Services await _databaseContext.AddAsync(newUser); await _databaseContext.SaveChangesAsync(); _logger.LogInformation(FormatLogMessage("A new user entry is added to the database.", Pair("Id", newUser.Id))); - return PutResult.Created; + return PutResult.Create; } user.EncryptedPassword = _passwordService.HashPassword(password); @@ -426,7 +275,7 @@ namespace Timeline.Services //clear cache RemoveCache(user.Id); - return PutResult.Modified; + return PutResult.Modify; } public async Task PatchUser(string username, string password, bool? administrator) @@ -504,7 +353,7 @@ namespace Timeline.Services if (string.IsNullOrEmpty(newUsername)) throw new ArgumentException("New username is null or empty", nameof(newUsername)); - if (!_usernameValidator.Validate(newUsername, out var message)) + if (!_usernameValidator.Validate(newUsername, _localizerFactory, out var message)) throw new UsernameBadFormatException(newUsername, $"New username is of bad format. {message}"); var user = await _databaseContext.Users.Where(u => u.Name == oldUsername).SingleOrDefaultAsync(); @@ -513,7 +362,7 @@ namespace Timeline.Services var conflictUser = await _databaseContext.Users.Where(u => u.Name == newUsername).SingleOrDefaultAsync(); if (conflictUser != null) - throw new UserAlreadyExistException(newUsername); + throw new UsernameConfictException(newUsername); user.Name = newUsername; user.Version += 1; diff --git a/Timeline/Services/UsernameBadFormatException.cs b/Timeline/Services/UsernameBadFormatException.cs new file mode 100644 index 00000000..04354d22 --- /dev/null +++ b/Timeline/Services/UsernameBadFormatException.cs @@ -0,0 +1,27 @@ +using System; + +namespace Timeline.Services +{ + /// + /// Thrown when username is of bad format. + /// + [Serializable] + public class UsernameBadFormatException : Exception + { + public UsernameBadFormatException() : base(Resources.Services.Exception.UsernameBadFormatException) { } + 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; } + + protected UsernameBadFormatException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + + /// + /// Username of bad format. + /// + public string? Username { get; private set; } + } +} diff --git a/Timeline/Services/UsernameConfictException.cs b/Timeline/Services/UsernameConfictException.cs new file mode 100644 index 00000000..fde1eda6 --- /dev/null +++ b/Timeline/Services/UsernameConfictException.cs @@ -0,0 +1,25 @@ +using System; +using Timeline.Helpers; + +namespace Timeline.Services +{ + /// + /// Thrown when the user already exists. + /// + [Serializable] + public class UsernameConfictException : Exception + { + public UsernameConfictException() : base(Resources.Services.Exception.UsernameConfictException) { } + public UsernameConfictException(string username) : base(Log.Format(Resources.Services.Exception.UsernameConfictException, ("Username", username))) { Username = username; } + public UsernameConfictException(string username, string message) : base(message) { Username = username; } + public UsernameConfictException(string message, Exception inner) : base(message, inner) { } + protected UsernameConfictException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + + /// + /// The username that already exists. + /// + public string? Username { get; set; } + } +} -- cgit v1.2.3