aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2019-04-21 23:23:49 +0800
committercrupest <crupest@outlook.com>2019-04-21 23:23:49 +0800
commite347b4a4092a24ff7106ffd3aca67d6ca7decca8 (patch)
treee139e794df8cd20c1cf4f60c668dd1d94bf239e1
parentfce9074be199b1c100481f49ccd9e231df2b84c8 (diff)
downloadtimeline-e347b4a4092a24ff7106ffd3aca67d6ca7decca8.tar.gz
timeline-e347b4a4092a24ff7106ffd3aca67d6ca7decca8.tar.bz2
timeline-e347b4a4092a24ff7106ffd3aca67d6ca7decca8.zip
Allow ordinary user to patch his password.
-rw-r--r--Timeline/Controllers/UserController.cs39
-rw-r--r--Timeline/Entities/AdminUser.cs30
-rw-r--r--Timeline/Entities/Common.cs12
-rw-r--r--Timeline/Entities/Token.cs26
-rw-r--r--Timeline/Entities/User.cs30
-rw-r--r--Timeline/Services/JwtService.cs22
-rw-r--r--Timeline/Services/UserService.cs21
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)