diff options
-rw-r--r-- | Timeline/Controllers/AdminUserController.cs | 83 | ||||
-rw-r--r-- | Timeline/Controllers/UserController.cs | 17 | ||||
-rw-r--r-- | Timeline/Entities/AdminUser.cs | 30 | ||||
-rw-r--r-- | Timeline/Entities/User.cs | 15 | ||||
-rw-r--r-- | Timeline/Services/UserService.cs | 155 |
5 files changed, 259 insertions, 41 deletions
diff --git a/Timeline/Controllers/AdminUserController.cs b/Timeline/Controllers/AdminUserController.cs new file mode 100644 index 00000000..7cc8c150 --- /dev/null +++ b/Timeline/Controllers/AdminUserController.cs @@ -0,0 +1,83 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Threading.Tasks; +using Timeline.Entities; +using Timeline.Services; + +namespace Timeline.Controllers +{ + [Route("admin")] + [Authorize(Roles = "admin")] + public class AdminUserController : Controller + { + private readonly IUserService _userService; + + public AdminUserController(IUserService userService) + { + _userService = userService; + } + + [HttpGet("users")] + public async Task<ActionResult<UserInfo[]>> List() + { + return Ok(await _userService.ListUsers()); + } + + [HttpGet("user/{username}")] + public async Task<IActionResult> Get([FromRoute] string username) + { + var user = await _userService.GetUser(username); + if (user == null) + { + return NotFound(); + } + return Ok(user); + } + + [HttpPut("user/{username}")] + public async Task<IActionResult> Put([FromBody] AdminUserEntityRequest request, [FromRoute] string username) + { + var result = await _userService.PutUser(username, request.Password, request.Roles); + switch (result) + { + case PutUserResult.Created: + return CreatedAtAction("Get", new { username }, AdminUserPutResponse.Created); + case PutUserResult.Modified: + return Ok(AdminUserPutResponse.Modified); + default: + throw new Exception("Unreachable code."); + } + } + + [HttpPatch("user/{username}")] + public async Task<IActionResult> Patch([FromBody] AdminUserEntityRequest request, [FromRoute] string username) + { + var result = await _userService.PatchUser(username, request.Password, request.Roles); + switch (result) + { + case PatchUserResult.Success: + return Ok(); + case PatchUserResult.NotExists: + return NotFound(); + default: + throw new Exception("Unreachable code."); + } + } + + [HttpDelete("user/{username}")] + public async Task<ActionResult<AdminUserDeleteResponse>> Delete([FromRoute] string username) + { + var result = await _userService.DeleteUser(username); + switch (result) + { + case DeleteUserResult.Success: + return Ok(AdminUserDeleteResponse.Success); + case DeleteUserResult.NotExists: + return Ok(AdminUserDeleteResponse.NotExists); + default: + throw new Exception("Uncreachable code."); + } + } + } +} diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs index 147724c1..285e0146 100644 --- a/Timeline/Controllers/UserController.cs +++ b/Timeline/Controllers/UserController.cs @@ -1,7 +1,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using System; using System.Threading.Tasks; using Timeline.Entities; using Timeline.Services; @@ -71,21 +70,5 @@ namespace Timeline.Controllers UserInfo = result }); } - - [HttpPost("[action]")] - [Authorize(Roles = "admin")] - public async Task<ActionResult<CreateUserResponse>> CreateUser([FromBody] CreateUserRequest request) - { - var result = await _userService.CreateUser(request.Username, request.Password, request.Roles); - switch (result) - { - case CreateUserResult.Success: - return Ok(new CreateUserResponse { ReturnCode = CreateUserResponse.SuccessCode }); - case CreateUserResult.AlreadyExists: - return Ok(new CreateUserResponse { ReturnCode = CreateUserResponse.AlreadyExistsCode }); - default: - throw new Exception("Unreachable code."); - } - } } } diff --git a/Timeline/Entities/AdminUser.cs b/Timeline/Entities/AdminUser.cs new file mode 100644 index 00000000..7b8b7fb7 --- /dev/null +++ b/Timeline/Entities/AdminUser.cs @@ -0,0 +1,30 @@ +namespace Timeline.Entities +{ + public class AdminUserEntityRequest + { + public string Password { get; set; } + public string[] Roles { get; set; } + } + + public class AdminUserPutResponse + { + public const int CreatedCode = 0; + public const int ModifiedCode = 1; + + public static AdminUserPutResponse Created { get; } = new AdminUserPutResponse { ReturnCode = CreatedCode }; + public static AdminUserPutResponse Modified { get; } = new AdminUserPutResponse { ReturnCode = ModifiedCode }; + + public int ReturnCode { get; set; } + } + + public class AdminUserDeleteResponse + { + public const int SuccessCode = 0; + public const int NotExistsCode = 1; + + public static AdminUserDeleteResponse Success { get; } = new AdminUserDeleteResponse { ReturnCode = SuccessCode }; + public static AdminUserDeleteResponse NotExists { get; } = new AdminUserDeleteResponse { ReturnCode = NotExistsCode }; + + public int ReturnCode { get; set; } + } +} diff --git a/Timeline/Entities/User.cs b/Timeline/Entities/User.cs index b5664bb0..1b5a469d 100644 --- a/Timeline/Entities/User.cs +++ b/Timeline/Entities/User.cs @@ -23,19 +23,4 @@ public bool IsValid { get; set; } public UserInfo UserInfo { get; set; } } - - public class CreateUserRequest - { - public string Username { get; set; } - public string Password { get; set; } - public string[] Roles { get; set; } - } - - public class CreateUserResponse - { - public const int SuccessCode = 0; - public const int AlreadyExistsCode = 1; - - public int ReturnCode { get; set; } - } } diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs index ad36c37b..caeb4efe 100644 --- a/Timeline/Services/UserService.cs +++ b/Timeline/Services/UserService.cs @@ -13,10 +13,40 @@ 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> + /// Succeed to delete user. + /// </summary> + Success, + /// <summary> + /// A user of given username does not exist. + /// </summary> + NotExists } public interface IUserService @@ -38,7 +68,51 @@ 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.Success"/> if success to delete. + /// 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.Success"/> if success to delete. + /// <see cref="DeleteUserResult.NotExists"/> if the user doesn't exist.</returns> + Task<DeleteUserResult> DeleteUser(string username); } public class UserService : IUserService @@ -108,19 +182,82 @@ namespace Timeline.Services return new UserInfo(user); } - public async Task<CreateUserResult> CreateUser(string username, string password, string[] roles) + public async Task<UserInfo> GetUser(string username) { - var exists = (await _databaseContext.Users.Where(u => u.Name == username).ToListAsync()).Count != 0; + return await _databaseContext.Users + .Where(user => user.Name == username) + .Select(user => new UserInfo(user)).SingleOrDefaultAsync(); + } - if (exists) + public async Task<UserInfo[]> ListUsers() + { + return await _databaseContext.Users.Select(user => new UserInfo(user)).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) { - return CreateUserResult.AlreadyExists; + await _databaseContext.AddAsync(new User + { + Name = username, + EncryptedPassword = _passwordService.HashPassword(password), + RoleString = string.Join(',', roles) + }); + await _databaseContext.SaveChangesAsync(); + return PutUserResult.Created; } - await _databaseContext.Users.AddAsync(new User { Name = username, EncryptedPassword = _passwordService.HashPassword(password), RoleString = string.Join(',', roles) }); + user.EncryptedPassword = _passwordService.HashPassword(password); + user.RoleString = string.Join(',', roles); await _databaseContext.SaveChangesAsync(); - return CreateUserResult.Success; + return PutUserResult.Modified; + } + + public async Task<PatchUserResult> PatchUser(string username, string password, string[] roles) + { + var user = await _databaseContext.Users.Where(u => u.Name == username).SingleOrDefaultAsync(); + + if (user == null) + return PatchUserResult.NotExists; + + bool modified = false; + + if (password != null) + { + modified = true; + user.EncryptedPassword = _passwordService.HashPassword(password); + } + + 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.Success; } } } |