diff options
author | 杨宇千 <crupest@outlook.com> | 2019-05-06 14:22:10 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-06 14:22:10 +0800 |
commit | 79dc38059819cf6dbcb8533b031f9a0dba8f6cb1 (patch) | |
tree | 37c2b6f9dcb3d6f57867ebc7538523d1a338183d /Timeline/Services/UserService.cs | |
parent | 1cb92b8f2a98005b793c00e0191903c0792d540a (diff) | |
parent | a04bcb5971872e7dbc079de9337875e73f7642dc (diff) | |
download | timeline-79dc38059819cf6dbcb8533b031f9a0dba8f6cb1.tar.gz timeline-79dc38059819cf6dbcb8533b031f9a0dba8f6cb1.tar.bz2 timeline-79dc38059819cf6dbcb8533b031f9a0dba8f6cb1.zip |
Merge pull request #22 from crupest/user-admin
Develop user management feature.
Diffstat (limited to 'Timeline/Services/UserService.cs')
-rw-r--r-- | Timeline/Services/UserService.cs | 275 |
1 files changed, 255 insertions, 20 deletions
diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs index ad36c37b..8ab3bc54 100644 --- a/Timeline/Services/UserService.cs +++ b/Timeline/Services/UserService.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using System; using System.Linq; using System.Threading.Tasks; using Timeline.Entities; @@ -13,10 +14,68 @@ namespace Timeline.Services public UserInfo UserInfo { get; set; } } - public enum CreateUserResult + public enum PutUserResult { + /// <summary> + /// A new user is created. + /// </summary> + Created, + /// <summary> + /// A existing user is modified. + /// </summary> + Modified + } + + public enum PatchUserResult + { + /// <summary> + /// Succeed to modify user. + /// </summary> Success, - AlreadyExists + /// <summary> + /// A user of given username does not exist. + /// </summary> + NotExists + } + + public enum DeleteUserResult + { + /// <summary> + /// A existing user is deleted. + /// </summary> + Deleted, + /// <summary> + /// A user of given username does not exist. + /// </summary> + NotExists + } + + public enum ChangePasswordResult + { + /// <summary> + /// Success to change password. + /// </summary> + Success, + /// <summary> + /// The user does not exists. + /// </summary> + NotExists, + /// <summary> + /// Old password is wrong. + /// </summary> + BadOldPassword + } + + public enum PutAvatarResult + { + /// <summary> + /// Success to upload avatar. + /// </summary> + Success, + /// <summary> + /// The user does not exists. + /// </summary> + UserNotExists } public interface IUserService @@ -38,7 +97,79 @@ namespace Timeline.Services /// <returns>Return null if verification failed. The user info if verification succeeded.</returns> Task<UserInfo> VerifyToken(string token); - Task<CreateUserResult> CreateUser(string username, string password, string[] roles); + /// <summary> + /// Get the user info of given username. + /// </summary> + /// <param name="username">Username of the user.</param> + /// <returns>The info of the user. Null if the user of given username does not exists.</returns> + Task<UserInfo> GetUser(string username); + + /// <summary> + /// List all users. + /// </summary> + /// <returns>The user info of users.</returns> + Task<UserInfo[]> ListUsers(); + + /// <summary> + /// Create or modify a user with given username. + /// Return <see cref="PutUserResult.Created"/> if a new user is created. + /// Return <see cref="PutUserResult.Modified"/> if a existing user is modified. + /// </summary> + /// <param name="username">Username of user.</param> + /// <param name="password">Password of user.</param> + /// <param name="roles">Array of roles of user.</param> + /// <returns>Return <see cref="PutUserResult.Created"/> if a new user is created. + /// Return <see cref="PutUserResult.Modified"/> if a existing user is modified.</returns> + Task<PutUserResult> PutUser(string username, string password, string[] roles); + + /// <summary> + /// Partially modify a use of given username. + /// </summary> + /// <param name="username">Username of the user to modify.</param> + /// <param name="password">New password. If not modify, then null.</param> + /// <param name="roles">New roles. If not modify, then null.</param> + /// <returns>Return <see cref="PatchUserResult.Success"/> if modification succeeds. + /// Return <see cref="PatchUserResult.NotExists"/> if the user of given username doesn't exist.</returns> + Task<PatchUserResult> PatchUser(string username, string password, string[] roles); + + /// <summary> + /// Delete a user of given username. + /// Return <see cref="DeleteUserResult.Deleted"/> if the user is deleted. + /// Return <see cref="DeleteUserResult.NotExists"/> if the user of given username + /// does not exist. + /// </summary> + /// <param name="username">Username of thet user to delete.</param> + /// <returns><see cref="DeleteUserResult.Deleted"/> if the user is deleted. + /// <see cref="DeleteUserResult.NotExists"/> if the user doesn't exist.</returns> + Task<DeleteUserResult> DeleteUser(string username); + + /// <summary> + /// Try to change a user's password with old password. + /// </summary> + /// <param name="username">The name of user to change password of.</param> + /// <param name="oldPassword">The user's old password.</param> + /// <param name="newPassword">The user's new password.</param> + /// <returns><see cref="ChangePasswordResult.Success"/> if success. + /// <see cref="ChangePasswordResult.NotExists"/> if user does not exist. + /// <see cref="ChangePasswordResult.BadOldPassword"/> if old password is wrong.</returns> + Task<ChangePasswordResult> ChangePassword(string username, string oldPassword, string newPassword); + + /// <summary> + /// Get the true avatar url of a user. + /// </summary> + /// <param name="username">The name of user.</param> + /// <returns>The url if user exists. Null if user does not exist.</returns> + Task<string> GetAvatarUrl(string username); + + /// <summary> + /// Put a avatar of a user. + /// </summary> + /// <param name="username">The name of user.</param> + /// <param name="data">The data of avatar image.</param> + /// <param name="mimeType">The mime type of the image.</param> + /// <returns>Return <see cref="PutAvatarResult.Success"/> if success. + /// Return <see cref="PutAvatarResult.UserNotExists"/> if user does not exist.</returns> + Task<PutAvatarResult> PutAvatar(string username, byte[] data, string mimeType); } public class UserService : IUserService @@ -47,19 +178,19 @@ namespace Timeline.Services private readonly DatabaseContext _databaseContext; private readonly IJwtService _jwtService; private readonly IPasswordService _passwordService; + private readonly IQCloudCosService _cosService; - public UserService(ILogger<UserService> logger, DatabaseContext databaseContext, IJwtService jwtService, IPasswordService passwordService) + public UserService(ILogger<UserService> logger, DatabaseContext databaseContext, IJwtService jwtService, IPasswordService passwordService, IQCloudCosService cosService) { _logger = logger; _databaseContext = databaseContext; _jwtService = jwtService; _passwordService = passwordService; + _cosService = cosService; } public async Task<CreateTokenResult> CreateToken(string username, string password) { - var users = _databaseContext.Users.ToList(); - var user = await _databaseContext.Users.Where(u => u.Name == username).SingleOrDefaultAsync(); if (user == null) @@ -72,11 +203,11 @@ namespace Timeline.Services if (verifyResult) { - var userInfo = new UserInfo(user); + var userInfo = UserInfo.Create(user); return new CreateTokenResult { - Token = _jwtService.GenerateJwtToken(user.Id, userInfo.Roles), + Token = _jwtService.GenerateJwtToken(user.Id, userInfo.Username, userInfo.Roles), UserInfo = userInfo }; } @@ -89,38 +220,142 @@ namespace Timeline.Services public async Task<UserInfo> VerifyToken(string token) { - var userId = _jwtService.VerifyJwtToken(token); + var userInfo = _jwtService.VerifyJwtToken(token); - if (userId == null) + if (userInfo == null) { _logger.LogInformation($"Verify token falied. Reason: invalid token. Token: {token} ."); return null; } - var user = await _databaseContext.Users.Where(u => u.Id == userId.Value).SingleOrDefaultAsync(); + return await Task.FromResult(userInfo); + } + + public async Task<UserInfo> GetUser(string username) + { + return await _databaseContext.Users + .Where(user => user.Name == username) + .Select(user => UserInfo.Create(user.Name, user.RoleString)) + .SingleOrDefaultAsync(); + } + + public async Task<UserInfo[]> ListUsers() + { + return await _databaseContext.Users + .Select(user => UserInfo.Create(user.Name, user.RoleString)) + .ToArrayAsync(); + } + + public async Task<PutUserResult> PutUser(string username, string password, string[] roles) + { + var user = await _databaseContext.Users.Where(u => u.Name == username).SingleOrDefaultAsync(); if (user == null) { - _logger.LogInformation($"Verify token falied. Reason: invalid user id. UserId: {userId} Token: {token} ."); - return null; + await _databaseContext.AddAsync(new User + { + Name = username, + EncryptedPassword = _passwordService.HashPassword(password), + RoleString = string.Join(',', roles) + }); + await _databaseContext.SaveChangesAsync(); + return PutUserResult.Created; } - return new UserInfo(user); + user.EncryptedPassword = _passwordService.HashPassword(password); + user.RoleString = string.Join(',', roles); + await _databaseContext.SaveChangesAsync(); + + return PutUserResult.Modified; } - public async Task<CreateUserResult> CreateUser(string username, string password, string[] roles) + public async Task<PatchUserResult> PatchUser(string username, string password, string[] roles) { - var exists = (await _databaseContext.Users.Where(u => u.Name == username).ToListAsync()).Count != 0; + var user = await _databaseContext.Users.Where(u => u.Name == username).SingleOrDefaultAsync(); - if (exists) + if (user == null) + return PatchUserResult.NotExists; + + bool modified = false; + + if (password != null) { - return CreateUserResult.AlreadyExists; + modified = true; + user.EncryptedPassword = _passwordService.HashPassword(password); } - await _databaseContext.Users.AddAsync(new User { Name = username, EncryptedPassword = _passwordService.HashPassword(password), RoleString = string.Join(',', roles) }); + if (roles != null) + { + modified = true; + user.RoleString = string.Join(',', roles); + } + + if (modified) + { + await _databaseContext.SaveChangesAsync(); + } + + return PatchUserResult.Success; + } + + public async Task<DeleteUserResult> DeleteUser(string username) + { + var user = await _databaseContext.Users.Where(u => u.Name == username).SingleOrDefaultAsync(); + + if (user == null) + { + return DeleteUserResult.NotExists; + } + + _databaseContext.Users.Remove(user); + await _databaseContext.SaveChangesAsync(); + return DeleteUserResult.Deleted; + } + + public async Task<ChangePasswordResult> ChangePassword(string username, string oldPassword, string newPassword) + { + var user = await _databaseContext.Users.Where(u => u.Name == username).SingleOrDefaultAsync(); + if (user == null) + return ChangePasswordResult.NotExists; + + var verifyResult = _passwordService.VerifyPassword(user.EncryptedPassword, oldPassword); + if (!verifyResult) + return ChangePasswordResult.BadOldPassword; + + user.EncryptedPassword = _passwordService.HashPassword(newPassword); await _databaseContext.SaveChangesAsync(); + return ChangePasswordResult.Success; + } + + public async Task<string> GetAvatarUrl(string username) + { + if (username == null) + throw new ArgumentNullException(nameof(username)); + + if ((await GetUser(username)) == null) + return null; + + var exists = await _cosService.IsObjectExists("avatar", username); + if (exists) + return _cosService.GenerateObjectGetUrl("avatar", username); + else + return _cosService.GenerateObjectGetUrl("avatar", "__default"); + } + + public async Task<PutAvatarResult> PutAvatar(string username, byte[] data, string mimeType) + { + if (username == null) + throw new ArgumentNullException(nameof(username)); + if (data == null) + throw new ArgumentNullException(nameof(data)); + if (mimeType == null) + throw new ArgumentNullException(nameof(mimeType)); + + if ((await GetUser(username)) == null) + return PutAvatarResult.UserNotExists; - return CreateUserResult.Success; + await _cosService.PutObject("avatar", username, data, mimeType); + return PutAvatarResult.Success; } } } |