From 81c261b59016e15d320ad963882afe4d9c7b5f7d Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 29 Jan 2020 18:38:41 +0800 Subject: ... --- Timeline/Services/UserService.cs | 263 ++++++++++++++++++++++++--------------- 1 file changed, 160 insertions(+), 103 deletions(-) (limited to 'Timeline/Services/UserService.cs') diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs index c5595c99..616e70ba 100644 --- a/Timeline/Services/UserService.cs +++ b/Timeline/Services/UserService.cs @@ -21,7 +21,7 @@ namespace Timeline.Services /// The password of the user to verify. /// The user info and auth info. /// Thrown when or is null. - /// Thrown when username is of bad format. + /// Thrown when is of bad format or is empty. /// Thrown when the user with given username does not exist. /// Thrown when password is wrong. Task VerifyCredential(string username, string password); @@ -48,7 +48,7 @@ namespace Timeline.Services /// List all users. /// /// The user info of users. - Task ListUsers(); + Task GetUsers(); /// /// Create a user with given info. @@ -58,11 +58,12 @@ namespace Timeline.Services /// The id of the new user. /// Thrown when is null. /// Thrown when some fields in is bad. - /// Thrown when a user with given username already exists. + /// Thrown when a user with given username already exists. /// /// must not be null and must be a valid username. /// must not be null or empty. /// is false by default (null). + /// must be a valid nickname if set. It is empty by default. /// Other fields are ignored. /// Task CreateUser(User info); @@ -75,61 +76,70 @@ namespace Timeline.Services /// Thrown when some fields in is bad. /// Thrown when user with given id does not exist. /// - /// Only , and will be used. + /// Only , , and will be used. /// If null, then not change. /// Other fields are ignored. /// After modified, even if nothing is changed, version will increase. /// - /// can't be empty. + /// must be a valid username if set. + /// can't be empty if set. + /// must be a valid nickname if set. /// /// Note: Whether is set or not, version will increase and not set to the specified value if there is one. /// + /// Task ModifyUser(long id, User? info); /// - /// Partially modify a user of given username. + /// Modify a user's info. + /// + /// The username of the user. + /// The new info. May be null. + /// Thrown when is null. + /// Thrown when is of bad format or some fields in is bad. + /// Thrown when user with given id does not exist. + /// + /// Only , and will be used. + /// If null, then not change. + /// Other fields are ignored. + /// After modified, even if nothing is changed, version will increase. + /// + /// must be a valid username if set. + /// can't be empty if set. + /// must be a valid nickname if set. /// - /// Note that whether actually modified or not, Version of the user will always increase. + /// Note: Whether is set or not, version will increase and not set to the specified value if there is one. + /// + /// + Task ModifyUser(string username, User? info); + + /// + /// Delete a user of given id. /// - /// Username of the user to modify. Can't be null. - /// New password. Null if not modify. - /// Whether the user is administrator. Null if not modify. - /// Thrown if is null. - /// Thrown when is of bad format. - /// Thrown if the user with given username does not exist. - Task PatchUser(string username, string? password, bool? administrator); + /// Id of the user to delete. + /// True if user is deleted, false if user not exist. + Task DeleteUser(long id); /// /// Delete a user of given username. /// - /// Username of thet user to delete. Can't be null. + /// Username of the user to delete. Can't be null. + /// True if user is deleted, false if user not exist. /// Thrown if is null. - /// Thrown when is of bad format. - /// Thrown if the user with given username does not exist. - Task DeleteUser(string username); + /// Thrown when is of bad format. + Task DeleteUser(string username); /// /// Try to change a user's password with old password. /// - /// The name of user to change password of. - /// The user's old password. - /// The user's new password. - /// Thrown if or or is null. - /// Thrown when is of bad format. + /// The id of user to change password of. + /// Old password. + /// New password. + /// Thrown if or is null. + /// Thrown if or is empty. /// Thrown if the user with given username does not exist. /// Thrown if the old password is wrong. - Task ChangePassword(string username, string oldPassword, string newPassword); - - /// - /// Change a user's username. - /// - /// The user's old username. - /// The new username. - /// Thrown if or is null. - /// Thrown if the user with old username does not exist. - /// Thrown if the or is of bad format. - /// Thrown if user with the new username already exists. - Task ChangeUsername(string oldUsername, string newUsername); + Task ChangePassword(long id, string oldPassword, string newPassword); } public class UserService : IUserService @@ -138,11 +148,10 @@ namespace Timeline.Services private readonly DatabaseContext _databaseContext; - private readonly IPasswordService _passwordService; private readonly UsernameValidator _usernameValidator = new UsernameValidator(); - + private readonly NicknameValidator _nicknameValidator = new NicknameValidator(); public UserService(ILogger logger, DatabaseContext databaseContext, IPasswordService passwordService) { _logger = logger; @@ -150,17 +159,35 @@ namespace Timeline.Services _passwordService = passwordService; } - private void CheckUsernameFormat(string username, string? paramName, Func? messageBuilder = null) + private void CheckUsernameFormat(string username, string? paramName) { if (!_usernameValidator.Validate(username, out var message)) { - if (messageBuilder == null) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionUsernameBadFormat, message), paramName); - else - throw new ArgumentException(messageBuilder(message), paramName); + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionUsernameBadFormat, message), paramName); + } + } + + private static void CheckPasswordFormat(string password, string? paramName) + { + if (password.Length == 0) + { + throw new ArgumentException(ExceptionPasswordEmpty, paramName); } } + private void CheckNicknameFormat(string nickname, string? paramName) + { + if (!_nicknameValidator.Validate(nickname, out var message)) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionNicknameBadFormat, message), paramName); + } + } + + private static void ThrowUsernameConflict() + { + throw new ConfictException(ExceptionUsernameConflict); + } + private static User CreateUserFromEntity(UserEntity entity) { return new User @@ -168,6 +195,7 @@ namespace Timeline.Services Username = entity.Username, Administrator = UserRoleConvert.ToBool(entity.Roles), Nickname = string.IsNullOrEmpty(entity.Nickname) ? entity.Username : entity.Nickname, + Id = entity.Id, Version = entity.Version }; } @@ -180,6 +208,7 @@ namespace Timeline.Services throw new ArgumentNullException(nameof(password)); CheckUsernameFormat(username, nameof(username)); + CheckPasswordFormat(password, nameof(password)); var entity = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync(); @@ -217,7 +246,7 @@ namespace Timeline.Services return CreateUserFromEntity(entity); } - public async Task ListUsers() + public async Task GetUsers() { var entities = await _databaseContext.Users.ToArrayAsync(); return entities.Select(user => CreateUserFromEntity(user)).ToArray(); @@ -228,20 +257,22 @@ namespace Timeline.Services if (info == null) throw new ArgumentNullException(nameof(info)); - if (string.IsNullOrEmpty(info.Username)) - throw new ArgumentException(ExceptionUsernameNullOrEmpty, nameof(info)); - + if (info.Username == null) + throw new ArgumentException(ExceptionUsernameNull, nameof(info)); CheckUsernameFormat(info.Username, nameof(info)); - if (string.IsNullOrEmpty(info.Password)) - throw new ArgumentException(ExceptionPasswordNullOrEmpty); + if (info.Password == null) + throw new ArgumentException(ExceptionPasswordNull, nameof(info)); + CheckPasswordFormat(info.Password, nameof(info)); + + if (info.Nickname != null) + CheckNicknameFormat(info.Nickname, nameof(info)); var username = info.Username; var conflict = await _databaseContext.Users.AnyAsync(u => u.Username == username); - if (conflict) - throw new UsernameConfictException(username); + ThrowUsernameConflict(); var administrator = info.Administrator ?? false; var password = info.Password; @@ -262,17 +293,35 @@ namespace Timeline.Services return newEntity.Id; } - public async Task ModifyUser(long id, User? info) + private void ValidateModifyUserInfo(User? info) { - if (info != null && info.Password != null && info.Password.Length == 0) - throw new ArgumentException(ExceptionPasswordEmpty, nameof(info)); + if (info != null) + { + if (info.Username != null) + CheckUsernameFormat(info.Username, nameof(info)); - var entity = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync(); - if (entity == null) - throw new UserNotExistException(id); + if (info.Password != null) + CheckPasswordFormat(info.Password, nameof(info)); + + if (info.Nickname != null) + CheckNicknameFormat(info.Nickname, nameof(info)); + } + } + private async Task UpdateUserEntity(UserEntity entity, User? info) + { if (info != null) { + var username = info.Username; + if (username != null) + { + var conflict = await _databaseContext.Users.AnyAsync(u => u.Username == username); + if (conflict) + ThrowUsernameConflict(); + + entity.Username = username; + } + var password = info.Password; if (password != null) { @@ -293,82 +342,90 @@ namespace Timeline.Services } entity.Version += 1; + } + + + public async Task ModifyUser(long id, User? info) + { + ValidateModifyUserInfo(info); + + var entity = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync(); + if (entity == null) + throw new UserNotExistException(id); + + await UpdateUserEntity(entity, info); await _databaseContext.SaveChangesAsync(); _logger.LogInformation(LogDatabaseUpdate, ("Id", id)); } - public async Task DeleteUser(string username) + public async Task ModifyUser(string username, User? info) { if (username == null) throw new ArgumentNullException(nameof(username)); - CheckUsernameFormat(username); + CheckUsernameFormat(username, nameof(username)); - var user = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync(); - if (user == null) + ValidateModifyUserInfo(info); + + var entity = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync(); + if (entity == null) throw new UserNotExistException(username); - _databaseContext.Users.Remove(user); + await UpdateUserEntity(entity, info); + await _databaseContext.SaveChangesAsync(); - _logger.LogInformation(Log.Format(Resources.Services.UserService.LogDatabaseRemove, - ("Id", user.Id))); + _logger.LogInformation(LogDatabaseUpdate, ("Username", username)); + } + + public async Task DeleteUser(long id) + { + var user = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync(); + if (user == null) + return false; - //clear cache - await _cache.RemoveCache(user.Id); + _databaseContext.Users.Remove(user); + await _databaseContext.SaveChangesAsync(); + _logger.LogInformation(Log.Format(LogDatabaseRemove, ("Id", id), ("Username", user.Username))); + return true; } - public async Task ChangePassword(string username, string oldPassword, string newPassword) + public async Task DeleteUser(string username) { if (username == null) throw new ArgumentNullException(nameof(username)); - if (oldPassword == null) - throw new ArgumentNullException(nameof(oldPassword)); - if (newPassword == null) - throw new ArgumentNullException(nameof(newPassword)); - CheckUsernameFormat(username); + CheckUsernameFormat(username, nameof(username)); var user = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync(); if (user == null) - throw new UserNotExistException(username); - - var verifyResult = _passwordService.VerifyPassword(user.Password, oldPassword); - if (!verifyResult) - throw new BadPasswordException(oldPassword); + return false; - user.Password = _passwordService.HashPassword(newPassword); - user.Version += 1; + _databaseContext.Users.Remove(user); await _databaseContext.SaveChangesAsync(); - _logger.LogInformation(Log.Format(Resources.Services.UserService.LogDatabaseUpdate, - ("Id", user.Id), ("Operation", "Change password"))); - //clear cache - await _cache.RemoveCache(user.Id); + _logger.LogInformation(Log.Format(LogDatabaseRemove, ("Id", user.Id), ("Username", username))); + return true; } - public async Task ChangeUsername(string oldUsername, string newUsername) + public async Task ChangePassword(long id, string oldPassword, string newPassword) { - if (oldUsername == null) - throw new ArgumentNullException(nameof(oldUsername)); - if (newUsername == null) - throw new ArgumentNullException(nameof(newUsername)); - CheckUsernameFormat(oldUsername, Resources.Services.UserService.ExceptionOldUsernameBadFormat); - CheckUsernameFormat(newUsername, Resources.Services.UserService.ExceptionNewUsernameBadFormat); - - var user = await _databaseContext.Users.Where(u => u.Username == oldUsername).SingleOrDefaultAsync(); - if (user == null) - throw new UserNotExistException(oldUsername); + if (oldPassword == null) + throw new ArgumentNullException(nameof(oldPassword)); + if (newPassword == null) + throw new ArgumentNullException(nameof(newPassword)); + CheckPasswordFormat(oldPassword, nameof(oldPassword)); + CheckPasswordFormat(newPassword, nameof(newPassword)); - var conflictUser = await _databaseContext.Users.Where(u => u.Username == newUsername).SingleOrDefaultAsync(); - if (conflictUser != null) - throw new UsernameConfictException(newUsername); + var entity = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync(); - user.Username = newUsername; - user.Version += 1; - await _databaseContext.SaveChangesAsync(); - _logger.LogInformation(Log.Format(Resources.Services.UserService.LogDatabaseUpdate, - ("Id", user.Id), ("Old Username", oldUsername), ("New Username", newUsername))); - await _cache.RemoveCache(user.Id); - } + if (entity == null) + throw new UserNotExistException(id); + if (!_passwordService.VerifyPassword(entity.Password, oldPassword)) + throw new BadPasswordException(oldPassword); + entity.Password = _passwordService.HashPassword(newPassword); + entity.Version += 1; + await _databaseContext.SaveChangesAsync(); + _logger.LogInformation(Log.Format(LogDatabaseUpdate, ("Id", id), ("Operation", "Change password"))); + } } } -- cgit v1.2.3