diff options
author | crupest <crupest@outlook.com> | 2019-04-21 23:23:49 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2019-04-21 23:23:49 +0800 |
commit | e347b4a4092a24ff7106ffd3aca67d6ca7decca8 (patch) | |
tree | e139e794df8cd20c1cf4f60c668dd1d94bf239e1 | |
parent | fce9074be199b1c100481f49ccd9e231df2b84c8 (diff) | |
download | timeline-e347b4a4092a24ff7106ffd3aca67d6ca7decca8.tar.gz timeline-e347b4a4092a24ff7106ffd3aca67d6ca7decca8.tar.bz2 timeline-e347b4a4092a24ff7106ffd3aca67d6ca7decca8.zip |
Allow ordinary user to patch his password.
-rw-r--r-- | Timeline/Controllers/UserController.cs | 39 | ||||
-rw-r--r-- | Timeline/Entities/AdminUser.cs | 30 | ||||
-rw-r--r-- | Timeline/Entities/Common.cs | 12 | ||||
-rw-r--r-- | Timeline/Entities/Token.cs | 26 | ||||
-rw-r--r-- | Timeline/Entities/User.cs | 30 | ||||
-rw-r--r-- | Timeline/Services/JwtService.cs | 22 | ||||
-rw-r--r-- | Timeline/Services/UserService.cs | 21 |
7 files changed, 103 insertions, 77 deletions
diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs index ab7e1b99..d2708eeb 100644 --- a/Timeline/Controllers/UserController.cs +++ b/Timeline/Controllers/UserController.cs @@ -48,18 +48,39 @@ namespace Timeline.Controllers } } - [HttpPatch("user/{username}"), Authorize(Roles = "admin")] + [HttpPatch("user/{username}"), Authorize] public async Task<IActionResult> Patch([FromBody] UserModifyRequest request, [FromRoute] string username) { - var result = await _userService.PatchUser(username, request.Password, request.Roles); - switch (result) + if (User.IsInRole("admin")) { - case PatchUserResult.Success: - return Ok(); - case PatchUserResult.NotExists: - return NotFound(); - default: - throw new Exception("Unreachable code."); + 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."); + } + } + else + { + if (User.Identity.Name != username) + return StatusCode(403, new MessageResponse("Can't patch other user when you are not admin.")); + if (request.Roles != null) + return StatusCode(403, new MessageResponse("Can't patch roles when you are not admin.")); + + var result = await _userService.PatchUser(username, request.Password, null); + switch (result) + { + case PatchUserResult.Success: + return Ok(); + case PatchUserResult.NotExists: + return NotFound(new MessageResponse("This username no longer exists. Please update your token.")); + default: + throw new Exception("Unreachable code."); + } } } diff --git a/Timeline/Entities/AdminUser.cs b/Timeline/Entities/AdminUser.cs deleted file mode 100644 index eb126165..00000000 --- a/Timeline/Entities/AdminUser.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Timeline.Entities -{ - public class UserModifyRequest - { - public string Password { get; set; } - public string[] Roles { get; set; } - } - - public class UserPutResponse - { - public const int CreatedCode = 0; - public const int ModifiedCode = 1; - - public static UserPutResponse Created { get; } = new UserPutResponse { ReturnCode = CreatedCode }; - public static UserPutResponse Modified { get; } = new UserPutResponse { ReturnCode = ModifiedCode }; - - public int ReturnCode { get; set; } - } - - public class UserDeleteResponse - { - public const int SuccessCode = 0; - public const int NotExistsCode = 1; - - public static UserDeleteResponse Success { get; } = new UserDeleteResponse { ReturnCode = SuccessCode }; - public static UserDeleteResponse NotExists { get; } = new UserDeleteResponse { ReturnCode = NotExistsCode }; - - public int ReturnCode { get; set; } - } -} diff --git a/Timeline/Entities/Common.cs b/Timeline/Entities/Common.cs new file mode 100644 index 00000000..235a2a20 --- /dev/null +++ b/Timeline/Entities/Common.cs @@ -0,0 +1,12 @@ +namespace Timeline.Entities +{ + public class MessageResponse + { + public MessageResponse(string message) + { + Message = message; + } + + public string Message { get; set; } + } +} diff --git a/Timeline/Entities/Token.cs b/Timeline/Entities/Token.cs new file mode 100644 index 00000000..1b5a469d --- /dev/null +++ b/Timeline/Entities/Token.cs @@ -0,0 +1,26 @@ +namespace Timeline.Entities +{ + public class CreateTokenRequest + { + public string Username { get; set; } + public string Password { get; set; } + } + + public class CreateTokenResponse + { + public bool Success { get; set; } + public string Token { get; set; } + public UserInfo UserInfo { get; set; } + } + + public class VerifyTokenRequest + { + public string Token { get; set; } + } + + public class VerifyTokenResponse + { + public bool IsValid { get; set; } + public UserInfo UserInfo { get; set; } + } +} diff --git a/Timeline/Entities/User.cs b/Timeline/Entities/User.cs index 1b5a469d..eb126165 100644 --- a/Timeline/Entities/User.cs +++ b/Timeline/Entities/User.cs @@ -1,26 +1,30 @@ namespace Timeline.Entities { - public class CreateTokenRequest + public class UserModifyRequest { - public string Username { get; set; } public string Password { get; set; } + public string[] Roles { get; set; } } - public class CreateTokenResponse + public class UserPutResponse { - public bool Success { get; set; } - public string Token { get; set; } - public UserInfo UserInfo { get; set; } - } + public const int CreatedCode = 0; + public const int ModifiedCode = 1; - public class VerifyTokenRequest - { - public string Token { get; set; } + public static UserPutResponse Created { get; } = new UserPutResponse { ReturnCode = CreatedCode }; + public static UserPutResponse Modified { get; } = new UserPutResponse { ReturnCode = ModifiedCode }; + + public int ReturnCode { get; set; } } - public class VerifyTokenResponse + public class UserDeleteResponse { - public bool IsValid { get; set; } - public UserInfo UserInfo { get; set; } + public const int SuccessCode = 0; + public const int NotExistsCode = 1; + + public static UserDeleteResponse Success { get; } = new UserDeleteResponse { ReturnCode = SuccessCode }; + public static UserDeleteResponse NotExists { get; } = new UserDeleteResponse { ReturnCode = NotExistsCode }; + + public int ReturnCode { get; set; } } } diff --git a/Timeline/Services/JwtService.cs b/Timeline/Services/JwtService.cs index 91e7f879..bf470354 100644 --- a/Timeline/Services/JwtService.cs +++ b/Timeline/Services/JwtService.cs @@ -7,25 +7,28 @@ using System.Linq; using System.Security.Claims; using System.Text; using Timeline.Configs; +using Timeline.Entities; namespace Timeline.Services { public interface IJwtService { /// <summary> - /// Create a JWT token for a given user id. + /// Create a JWT token for a given user info. /// </summary> - /// <param name="userId">The user id used to generate token.</param> + /// <param name="userId">The user id contained in generate token.</param> + /// <param name="username">The username contained in token.</param> + /// <param name="roles">The roles contained in token.</param> /// <returns>Return the generated token.</returns> - string GenerateJwtToken(long userId, string[] roles); + string GenerateJwtToken(long userId, string username, string[] roles); /// <summary> /// Verify a JWT token. /// Return null is <paramref name="token"/> is null. /// </summary> /// <param name="token">The token string to verify.</param> - /// <returns>Return null if <paramref name="token"/> is null or token is invalid. Return the saved user id otherwise.</returns> - long? VerifyJwtToken(string token); + /// <returns>Return null if <paramref name="token"/> is null or token is invalid. Return the saved user info otherwise.</returns> + UserInfo VerifyJwtToken(string token); } @@ -41,12 +44,13 @@ namespace Timeline.Services _logger = logger; } - public string GenerateJwtToken(long id, string[] roles) + public string GenerateJwtToken(long id, string username, string[] roles) { var jwtConfig = _jwtConfig.CurrentValue; var identity = new ClaimsIdentity(); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, id.ToString())); + identity.AddClaim(new Claim(identity.NameClaimType, username)); identity.AddClaims(roles.Select(role => new Claim(identity.RoleClaimType, role))); var tokenDescriptor = new SecurityTokenDescriptor() @@ -67,13 +71,12 @@ namespace Timeline.Services } - public long? VerifyJwtToken(string token) + public UserInfo VerifyJwtToken(string token) { if (token == null) return null; var config = _jwtConfig.CurrentValue; - try { var principal = _tokenHandler.ValidateToken(token, new TokenValidationParameters @@ -87,7 +90,8 @@ namespace Timeline.Services IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(config.SigningKey)) }, out SecurityToken validatedToken); - return long.Parse(principal.FindAll(ClaimTypes.NameIdentifier).Single().Value); + return new UserInfo(principal.Identity.Name, + principal.FindAll(ClaimTypes.Role).Select(c => c.Value).ToArray()); } catch (Exception e) { diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs index 34eeb1ad..a0d358dd 100644 --- a/Timeline/Services/UserService.cs +++ b/Timeline/Services/UserService.cs @@ -101,7 +101,7 @@ namespace Timeline.Services /// <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); + Task<PatchUserResult> PatchUser(string username, string password, string[] roles); /// <summary> /// Delete a user of given username. @@ -148,7 +148,7 @@ namespace Timeline.Services return new CreateTokenResult { - Token = _jwtService.GenerateJwtToken(user.Id, userInfo.Roles), + Token = _jwtService.GenerateJwtToken(user.Id, userInfo.Username, userInfo.Roles), UserInfo = userInfo }; } @@ -161,26 +161,15 @@ 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) - .Select(u => UserInfo.Create(u.Name, u.RoleString)) - .SingleOrDefaultAsync(); - - if (user == null) - { - _logger.LogInformation($"Verify token falied. Reason: invalid user id. UserId: {userId} Token: {token} ."); - return null; - } - - return user; + return await Task.FromResult(userInfo); } public async Task<UserInfo> GetUser(string username) |