aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortranstrike <transtrike@gmail.com>2021-02-28 13:00:16 +0200
committertranstrike <transtrike@gmail.com>2021-02-28 13:00:16 +0200
commit26b18fe3727507d1b47ffb53ed773f133122eee8 (patch)
treecad0cdb64cd98edf1ced707b2296fb16da505801
parente4331fe503547df8f17095540cbd4170bbaf2b25 (diff)
downloadDevHive-26b18fe3727507d1b47ffb53ed773f133122eee8.tar
DevHive-26b18fe3727507d1b47ffb53ed773f133122eee8.tar.gz
DevHive-26b18fe3727507d1b47ffb53ed773f133122eee8.zip
Integrated new JWT validation where needed
-rw-r--r--src/.editorconfig5
-rw-r--r--src/Common/DevHive.Common/Jwt/Interfaces/IJwtService.cs17
-rw-r--r--src/Common/DevHive.Common/Jwt/JwtService.cs2
-rw-r--r--src/Services/DevHive.Services/Interfaces/IUserService.cs49
-rw-r--r--src/Services/DevHive.Services/Services/UserService.cs117
-rw-r--r--src/Web/DevHive.Web/Controllers/CommentController.cs10
-rw-r--r--src/Web/DevHive.Web/Controllers/PostController.cs10
-rw-r--r--src/Web/DevHive.Web/Controllers/UserController.cs12
8 files changed, 95 insertions, 127 deletions
diff --git a/src/.editorconfig b/src/.editorconfig
index ea2af21..9f0e74b 100644
--- a/src/.editorconfig
+++ b/src/.editorconfig
@@ -44,9 +44,10 @@ dotnet_diagnostic.IDE0055.severity = warning
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false
+
# Avoid "this." and "Me." if not necessary
-dotnet_style_qualification_for_field = false:refactoring
-dotnet_style_qualification_for_property = false:refactoring
+dotnet_style_qualification_for_field = true:refactoring
+dotnet_style_qualification_for_property = true:refactoring
dotnet_style_qualification_for_method = false:refactoring
dotnet_style_qualification_for_event = false:refactoring
diff --git a/src/Common/DevHive.Common/Jwt/Interfaces/IJwtService.cs b/src/Common/DevHive.Common/Jwt/Interfaces/IJwtService.cs
index 6f844f5..352a7d5 100644
--- a/src/Common/DevHive.Common/Jwt/Interfaces/IJwtService.cs
+++ b/src/Common/DevHive.Common/Jwt/Interfaces/IJwtService.cs
@@ -5,7 +5,24 @@ namespace DevHive.Common.Jwt.Interfaces
{
public interface IJwtService
{
+ /// <summary>
+ /// The generation of a JWT, when a new user registers or log ins
+ /// Tokens have an expiration time of 7 days.
+ /// </summary>
+ /// <param name="userId">User's Guid</param>
+ /// <param name="username">Users's username</param>
+ /// <param name="roleNames">List of user's roles</param>
+ /// <returns>Return a new JWT, containing the user id, username and roles.</returns>
string GenerateJwtToken(Guid userId, string username, List<string> roleNames);
+
+ /// <summary>
+ /// Checks whether the given user, gotten by the "id" property,
+ /// is the same user as the one in the token (unless the user in the token has the admin role)
+ /// and the roles in the token are the same as those in the user, gotten by the id in the token
+ /// </summary>
+ /// <param name="userId">Guid of the user being validated</param>
+ /// <param name="rawToken">The raw token coming from the request</param>
+ /// <returns>Bool result of is the user authenticated to do an action</returns>
bool ValidateToken(Guid userId, string rawToken);
}
}
diff --git a/src/Common/DevHive.Common/Jwt/JwtService.cs b/src/Common/DevHive.Common/Jwt/JwtService.cs
index a0c49db..9f316da 100644
--- a/src/Common/DevHive.Common/Jwt/JwtService.cs
+++ b/src/Common/DevHive.Common/Jwt/JwtService.cs
@@ -1,11 +1,9 @@
using System;
-using System.Buffers.Text;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
-using System.Text;
using DevHive.Common.Jwt.Interfaces;
using Microsoft.IdentityModel.Tokens;
diff --git a/src/Services/DevHive.Services/Interfaces/IUserService.cs b/src/Services/DevHive.Services/Interfaces/IUserService.cs
index 4a9ffc8..a55f9dd 100644
--- a/src/Services/DevHive.Services/Interfaces/IUserService.cs
+++ b/src/Services/DevHive.Services/Interfaces/IUserService.cs
@@ -7,19 +7,64 @@ namespace DevHive.Services.Interfaces
{
public interface IUserService
{
+ /// <summary>
+ /// Log ins an existing user and gives him/her a JWT Token for further authorization
+ /// </summary>
+ /// <param name="loginModel">Login service model, conaining user's username and password</param>
+ /// <returns>A JWT Token for authorization</returns>
Task<TokenModel> LoginUser(LoginServiceModel loginModel);
+
+ /// <summary>
+ /// Registers a new user and gives him/her a JWT Token for further authorization
+ /// </summary>
+ /// <param name="registerModel">Register service model, containing the new user's data</param>
+ /// <returns>A JWT Token for authorization</returns>
Task<TokenModel> RegisterUser(RegisterServiceModel registerModel);
+ /// <summary>
+ /// Get a user by his username. Used for querying profiles without provided authentication
+ /// </summary>
+ /// <param name="username">User's username, who's to be queried</param>
+ /// <returns>The queried user or null, if non existant</returns>
Task<UserServiceModel> GetUserByUsername(string username);
+
+ /// <summary>
+ /// Get a user by his Guid. Used for querying full user's profile
+ /// Requires authenticated user
+ /// </summary>
+ /// <param name="id">User's username, who's to be queried</param>
+ /// <returns>The queried user or null, if non existant</returns>
Task<UserServiceModel> GetUserById(Guid id);
+ /// <summary>
+ /// Updates a user's data, provided a full model with new details
+ /// Requires authenticated user
+ /// </summary>
+ /// <param name="updateUserServiceModel">Full update user model for updating</param>
+ /// <returns>Read model of the new user</returns>
Task<UserServiceModel> UpdateUser(UpdateUserServiceModel updateUserServiceModel);
+
+ /// <summary>
+ /// Uploads the given picture and assigns it's link to the user in the database
+ /// Requires authenticated user
+ /// </summary>
+ /// <param name="updateProfilePictureServiceModel">Contains User's Guid and the new picture to be updated</param>
+ /// <returns>The new picture's URL</returns>
Task<ProfilePictureServiceModel> UpdateProfilePicture(UpdateProfilePictureServiceModel updateProfilePictureServiceModel);
+ /// <summary>
+ /// Deletes a user from the database and removes his data entirely
+ /// Requires authenticated user
+ /// </summary>
+ /// <param name="id">The user's Guid, who's to be deleted</param>
+ /// <returns>True if successfull, false otherwise</returns>
Task<bool> DeleteUser(Guid id);
- Task<bool> ValidJWT(Guid id, string rawTokenData);
-
+ /// <summary>
+ /// We don't talk about that!
+ /// </summary>
+ /// <param name="userId"></param>
+ /// <returns></returns>
Task<TokenModel> SuperSecretPromotionToAdmin(Guid userId);
}
}
diff --git a/src/Services/DevHive.Services/Services/UserService.cs b/src/Services/DevHive.Services/Services/UserService.cs
index cbcb116..4f74b06 100644
--- a/src/Services/DevHive.Services/Services/UserService.cs
+++ b/src/Services/DevHive.Services/Services/UserService.cs
@@ -1,20 +1,14 @@
using AutoMapper;
-using DevHive.Services.Options;
using DevHive.Services.Models.User;
using System.Threading.Tasks;
using DevHive.Data.Models;
using System;
-using System.IdentityModel.Tokens.Jwt;
-using System.Security.Claims;
-using Microsoft.IdentityModel.Tokens;
-using System.Text;
using System.Collections.Generic;
using DevHive.Common.Models.Identity;
using DevHive.Services.Interfaces;
using DevHive.Data.Interfaces;
using System.Linq;
using Microsoft.AspNetCore.Http;
-using DevHive.Common.Jwt;
using DevHive.Common.Jwt.Interfaces;
namespace DevHive.Services.Services
@@ -26,7 +20,6 @@ namespace DevHive.Services.Services
private readonly ILanguageRepository _languageRepository;
private readonly ITechnologyRepository _technologyRepository;
private readonly IMapper _userMapper;
- private readonly JwtOptions _jwtOptions;
private readonly ICloudService _cloudService;
private readonly IJwtService _jwtService;
@@ -35,14 +28,12 @@ namespace DevHive.Services.Services
IRoleRepository roleRepository,
ITechnologyRepository technologyRepository,
IMapper mapper,
- JwtOptions jwtOptions,
ICloudService cloudService,
IJwtService jwtService)
{
this._userRepository = userRepository;
this._roleRepository = roleRepository;
this._userMapper = mapper;
- this._jwtOptions = jwtOptions;
this._languageRepository = languageRepository;
this._technologyRepository = technologyRepository;
this._cloudService = cloudService;
@@ -50,10 +41,6 @@ namespace DevHive.Services.Services
}
#region Authentication
- /// <summary>
- /// Adds a new user to the database with the values from the given model.
- /// Returns a JSON Web Token (that can be used for authorization)
- /// </summary>
public async Task<TokenModel> LoginUser(LoginServiceModel loginModel)
{
if (!await this._userRepository.DoesUsernameExistAsync(loginModel.UserName))
@@ -64,14 +51,10 @@ namespace DevHive.Services.Services
if (!await this._userRepository.VerifyPassword(user, loginModel.Password))
throw new ArgumentException("Incorrect password!");
- return new TokenModel(WriteJWTSecurityToken(user.Id, user.UserName, user.Roles));
+ List<string> roleNames = user.Roles.Select(x => x.Name).ToList();
+ return new TokenModel(this._jwtService.GenerateJwtToken(user.Id, user.UserName, roleNames));
}
- /// <summary>
- /// Register a user in the database and return a
- /// </summary>
- /// <param name="registerModel">Register model, containing registration information</param>
- /// <returns>A Token model, containing JWT Token for further verification</returns>
public async Task<TokenModel> RegisterUser(RegisterServiceModel registerModel)
{
if (await this._userRepository.DoesUsernameExistAsync(registerModel.UserName))
@@ -91,11 +74,8 @@ namespace DevHive.Services.Services
throw new ArgumentException("Unable to add role to user");
User createdUser = await this._userRepository.GetByUsernameAsync(registerModel.UserName);
- List<string> roleNames = createdUser
- .Roles
- .Select(x => x.Name)
- .ToList();
+ List<string> roleNames = createdUser.Roles.Select(x => x.Name).ToList();
return new TokenModel(this._jwtService.GenerateJwtToken(createdUser.Id, createdUser.UserName, roleNames));
}
#endregion
@@ -140,9 +120,6 @@ namespace DevHive.Services.Services
return this._userMapper.Map<UserServiceModel>(newUser);
}
- /// <summary>
- /// Uploads the given picture and assigns it's link to the user in the database
- /// </summary>
public async Task<ProfilePictureServiceModel> UpdateProfilePicture(UpdateProfilePictureServiceModel updateProfilePictureServiceModel)
{
User user = await this._userRepository.GetByIdAsync(updateProfilePictureServiceModel.UserId);
@@ -179,61 +156,7 @@ namespace DevHive.Services.Services
#region Validations
/// <summary>
- /// Checks whether the given user, gotten by the "id" property,
- /// is the same user as the one in the token (unless the user in the token has the admin role)
- /// and the roles in the token are the same as those in the user, gotten by the id in the token
- /// </summary>
- /// <param name="id"></param>
- /// <param name="rawTokenData"></param>
- /// <returns></returns>
- public async Task<bool> ValidJWT(Guid id, string rawTokenData)
- {
- return this._jwtService.ValidateToken(rawTokenData);
- // There is authorization name in the beginning, i.e. "Bearer eyJh..."
- // var jwt = new JwtSecurityTokenHandler().ReadJwtToken(rawTokenData.Remove(0, 7));
-
- // Guid jwtUserID = new(UserService.GetClaimTypeValues("ID", jwt.Claims).First());
- // List<string> jwtRoleNames = UserService.GetClaimTypeValues("role", jwt.Claims);
-
- // User user = await this._userRepository.GetByIdAsync(jwtUserID)
- // ?? throw new ArgumentException("User does not exist!");
-
- // /* Check if he is an admin */
- // if (user.Roles.Any(x => x.Name == Role.AdminRole))
- // return true;
-
- // if (!jwtRoleNames.Contains(Role.AdminRole) && user.Id != id)
- // return false;
-
- // // Check if jwt contains all user roles (if it doesn't, jwt is either old or tampered with)
- // foreach (var role in user.Roles)
- // if (!jwtRoleNames.Contains(role.Name))
- // return false;
-
- // // Check if jwt contains only roles of user
- // if (jwtRoleNames.Count != user.Roles.Count)
- // return false;
-
- // return true;
- }
-
- /// <summary>
- /// Returns all values from a given claim type
- /// </summary>
- private static List<string> GetClaimTypeValues(string type, IEnumerable<Claim> claims)
- {
- List<string> toReturn = new();
-
- foreach (var claim in claims)
- if (claim.Type == type)
- toReturn.Add(claim.Value);
-
- return toReturn;
- }
-
- /// <summary>
- /// Checks whether the user in the model exists
- /// and whether the username in the model is already taken.
+ /// Checks whether the user in the model exists and whether the username in the model is already taken.
/// If the check fails (is false), it throws an exception, otherwise nothing happens
/// </summary>
private async Task ValidateUserOnUpdate(UpdateUserServiceModel updateUserServiceModel)
@@ -255,38 +178,6 @@ namespace DevHive.Services.Services
if (!await this._userRepository.ValidateFriendsCollectionAsync(usernames))
throw new ArgumentException("One or more friends do not exist!");
}
-
- /// <summary>
- /// Return a new JSON Web Token, containing the user id, username and roles.
- /// Tokens have an expiration time of 7 days.
- /// </summary>
- private string WriteJWTSecurityToken(Guid userId, string username, HashSet<Role> roles)
- {
- byte[] signingKey = Encoding.ASCII.GetBytes(_jwtOptions.Secret);
- HashSet<Claim> claims = new()
- {
- new Claim("ID", $"{userId}"),
- new Claim("Username", username)
- };
-
- foreach (var role in roles)
- {
- claims.Add(new Claim(ClaimTypes.Role, role.Name));
- }
-
- SecurityTokenDescriptor tokenDescriptor = new()
- {
- Subject = new ClaimsIdentity(claims),
- Expires = DateTime.Today.AddDays(7),
- SigningCredentials = new SigningCredentials(
- new SymmetricSecurityKey(signingKey),
- SecurityAlgorithms.HmacSha512Signature)
- };
-
- JwtSecurityTokenHandler tokenHandler = new();
- SecurityToken token = tokenHandler.CreateToken(tokenDescriptor);
- return tokenHandler.WriteToken(token);
- }
#endregion
#region Misc
diff --git a/src/Web/DevHive.Web/Controllers/CommentController.cs b/src/Web/DevHive.Web/Controllers/CommentController.cs
index c38e300..b4fae5c 100644
--- a/src/Web/DevHive.Web/Controllers/CommentController.cs
+++ b/src/Web/DevHive.Web/Controllers/CommentController.cs
@@ -6,6 +6,7 @@ using DevHive.Web.Models.Comment;
using DevHive.Services.Models.Comment;
using Microsoft.AspNetCore.Authorization;
using DevHive.Services.Interfaces;
+using DevHive.Common.Jwt.Interfaces;
namespace DevHive.Web.Controllers
{
@@ -16,16 +17,21 @@ namespace DevHive.Web.Controllers
{
private readonly ICommentService _commentService;
private readonly IMapper _commentMapper;
+ private readonly IJwtService _jwtService;
- public CommentController(ICommentService commentService, IMapper commentMapper)
+ public CommentController(ICommentService commentService, IMapper commentMapper, IJwtService jwtService)
{
this._commentService = commentService;
this._commentMapper = commentMapper;
+ this._jwtService = jwtService;
}
[HttpPost]
public async Task<IActionResult> AddComment(Guid userId, [FromBody] CreateCommentWebModel createCommentWebModel, [FromHeader] string authorization)
{
+ if (!this._jwtService.ValidateToken(userId, authorization))
+ return new UnauthorizedResult();
+
if (!await this._commentService.ValidateJwtForCreating(userId, authorization))
return new UnauthorizedResult();
@@ -53,7 +59,7 @@ namespace DevHive.Web.Controllers
[HttpPut]
public async Task<IActionResult> UpdateComment(Guid userId, [FromBody] UpdateCommentWebModel updateCommentWebModel, [FromHeader] string authorization)
{
- if (!await this._commentService.ValidateJwtForComment(updateCommentWebModel.CommentId, authorization))
+ if (!this._jwtService.ValidateToken(userId, authorization))
return new UnauthorizedResult();
UpdateCommentServiceModel updateCommentServiceModel =
diff --git a/src/Web/DevHive.Web/Controllers/PostController.cs b/src/Web/DevHive.Web/Controllers/PostController.cs
index d3fdbf6..309070c 100644
--- a/src/Web/DevHive.Web/Controllers/PostController.cs
+++ b/src/Web/DevHive.Web/Controllers/PostController.cs
@@ -6,6 +6,7 @@ using DevHive.Web.Models.Post;
using DevHive.Services.Models.Post;
using Microsoft.AspNetCore.Authorization;
using DevHive.Services.Interfaces;
+using DevHive.Common.Jwt.Interfaces;
namespace DevHive.Web.Controllers
{
@@ -16,18 +17,20 @@ namespace DevHive.Web.Controllers
{
private readonly IPostService _postService;
private readonly IMapper _postMapper;
+ private readonly IJwtService _jwtService;
- public PostController(IPostService postService, IMapper postMapper)
+ public PostController(IPostService postService, IMapper postMapper, IJwtService jwtService)
{
this._postService = postService;
this._postMapper = postMapper;
+ this._jwtService = jwtService;
}
#region Create
[HttpPost]
public async Task<IActionResult> Create(Guid userId, [FromForm] CreatePostWebModel createPostWebModel, [FromHeader] string authorization)
{
- if (!await this._postService.ValidateJwtForCreating(userId, authorization))
+ if (!this._jwtService.ValidateToken(userId, authorization))
return new UnauthorizedResult();
CreatePostServiceModel createPostServiceModel =
@@ -58,6 +61,9 @@ namespace DevHive.Web.Controllers
[HttpPut]
public async Task<IActionResult> Update(Guid userId, [FromForm] UpdatePostWebModel updatePostWebModel, [FromHeader] string authorization)
{
+ if (!this._jwtService.ValidateToken(userId, authorization))
+ return new UnauthorizedResult();
+
if (!await this._postService.ValidateJwtForPost(updatePostWebModel.PostId, authorization))
return new UnauthorizedResult();
diff --git a/src/Web/DevHive.Web/Controllers/UserController.cs b/src/Web/DevHive.Web/Controllers/UserController.cs
index a1e87f4..b01ecc1 100644
--- a/src/Web/DevHive.Web/Controllers/UserController.cs
+++ b/src/Web/DevHive.Web/Controllers/UserController.cs
@@ -7,6 +7,8 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using DevHive.Common.Models.Identity;
using DevHive.Services.Interfaces;
+using DevHive.Common.Jwt.Interfaces;
+using DevHive.Web.Models.Attributes;
namespace DevHive.Web.Controllers
{
@@ -16,11 +18,13 @@ namespace DevHive.Web.Controllers
{
private readonly IUserService _userService;
private readonly IMapper _userMapper;
+ private readonly IJwtService _jwtService;
- public UserController(IUserService userService, IMapper mapper)
+ public UserController(IUserService userService, IMapper mapper, IJwtService jwtService)
{
this._userService = userService;
this._userMapper = mapper;
+ this._jwtService = jwtService;
}
#region Authentication
@@ -56,7 +60,7 @@ namespace DevHive.Web.Controllers
[Authorize(Roles = "User,Admin")]
public async Task<IActionResult> GetById(Guid id, [FromHeader] string authorization)
{
- if (!await this._userService.ValidJWT(id, authorization))
+ if (!this._jwtService.ValidateToken(id, authorization))
return new UnauthorizedResult();
UserServiceModel userServiceModel = await this._userService.GetUserById(id);
@@ -82,7 +86,7 @@ namespace DevHive.Web.Controllers
[Authorize(Roles = "User,Admin")]
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateUserWebModel updateUserWebModel, [FromHeader] string authorization)
{
- if (!await this._userService.ValidJWT(id, authorization))
+ if (!this._jwtService.ValidateToken(id, authorization))
return new UnauthorizedResult();
UpdateUserServiceModel updateUserServiceModel = this._userMapper.Map<UpdateUserServiceModel>(updateUserWebModel);
@@ -100,7 +104,7 @@ namespace DevHive.Web.Controllers
[Authorize(Roles = "User,Admin")]
public async Task<IActionResult> Delete(Guid id, [FromHeader] string authorization)
{
- if (!await this._userService.ValidJWT(id, authorization))
+ if (!this._jwtService.ValidateToken(id, authorization))
return new UnauthorizedResult();
bool result = await this._userService.DeleteUser(id);