diff --git a/MainProject.Tests/MainProject.Tests.csproj b/MainProject.Tests/MainProject.Tests.csproj
index b083669..975aa5a 100644
--- a/MainProject.Tests/MainProject.Tests.csproj
+++ b/MainProject.Tests/MainProject.Tests.csproj
@@ -14,11 +14,11 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
-
-
+
+
diff --git a/MainProject.Tests/Services/UserService_Tests.cs b/MainProject.Tests/Services/UserService_Tests.cs
index 80f5ae1..ce5f670 100644
--- a/MainProject.Tests/Services/UserService_Tests.cs
+++ b/MainProject.Tests/Services/UserService_Tests.cs
@@ -110,7 +110,7 @@ public class UserService_Tests
if (_userService != null)
{
var user = await _userService.GetUserByUsernameAndPassword(_user.Email, "WrongPassword");
- Assert.IsTrue(user == null);
+ Assert.IsNull(user);
}
else
{
@@ -133,7 +133,7 @@ public class UserService_Tests
if (_userService != null)
{
var user = await _userService.GetUserByUsernameAndPassword(_user.Email, password);
- Assert.IsTrue(user != null);
+ Assert.IsNotNull(user);
}
else
{
@@ -250,7 +250,7 @@ public class UserService_Tests
{
var user = await _userService.GetUserByIdAsync(_user.Id);
Assert.IsNotNull(user);
- Assert.IsTrue(user.Id == _user?.Id);
+ Assert.AreEqual(user.Id, _user?.Id);
}
else
{
@@ -274,7 +274,7 @@ public class UserService_Tests
{
var user = await _userService.GetUserByGuidAsync(_user.Guid ?? String.Empty);
Assert.IsNotNull(user);
- Assert.IsTrue(user.Guid == _user?.Guid);
+ Assert.AreEqual(user.Guid, _user?.Guid);
}
else
{
@@ -288,6 +288,186 @@ public class UserService_Tests
}
}
+ [TestMethod]
+ public async Task UpdateUserAsync_Success()
+ {
+ try
+ {
+ UpdateUserRequestData data = new UpdateUserRequestData()
+ {
+ FirstName = "ChangedUserFirstName",
+ LastName = "ChangedUserLastName"
+ };
+
+ if (_userService != null)
+ {
+ Assert.IsNotNull(_user);
+ var user = await _userService.UpdateUserAsync(data, _user!);
+ Assert.IsInstanceOfType(user, typeof(User));
+ Assert.IsNotNull(user);
+ Assert.AreEqual(data.FirstName, user.FirstName);
+ Assert.AreEqual(data.LastName, user.LastName);
+ _user = user;
+ }
+ else
+ {
+ Assert.Fail($"UserService is null");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex.InnerException);
+ Assert.Fail($"An exception was thrown: {ex}");
+ }
+ }
+
+ [TestMethod]
+ public async Task UpdateUserAsync_Exception()
+ {
+ try
+ {
+ UpdateUserRequestData data = new UpdateUserRequestData()
+ {
+ FirstName = "ChangedUserFirstName",
+ LastName = "ChangedUserLastName"
+ };
+
+ var exceptionUserService = TestUtils.CreateUserServiceException();
+
+ if (exceptionUserService != null)
+ {
+ Assert.IsNotNull(_user);
+ var user = await exceptionUserService.UpdateUserAsync(data, _user!);
+ Assert.Fail($"Expected exception instead of response: {user?.Guid}");
+
+ }
+ else
+ {
+ Assert.Fail($"UserService is null");
+ }
+ }
+ catch (Exception ex)
+ {
+ Assert.IsInstanceOfType(ex, typeof(Exception));
+ }
+ }
+
+
+ [TestMethod]
+ public async Task UpdateUserPasswordAsync_Success()
+ {
+ try
+ {
+ if (_userService != null)
+ {
+ Assert.IsNotNull(_user);
+ var oldPassword = _user.Password;
+ var user = await _userService.UpdateUserPasswordAsync(_user!, "this-is-a-new-password");
+ Assert.IsInstanceOfType(user, typeof(User));
+ Assert.IsNotNull(user);
+ Assert.IsTrue(user.Password != oldPassword);
+ }
+ else
+ {
+ Assert.Fail($"UserService is null");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex.InnerException);
+ Assert.Fail($"An exception was thrown: {ex}");
+ }
+ }
+
+ [TestMethod]
+ public async Task UpdateUserPasswordAsync_Exception()
+ {
+ try
+ {
+ var exceptionUserService = TestUtils.CreateUserServiceException();
+
+ if (exceptionUserService != null)
+ {
+ Assert.IsNotNull(_user);
+ var user = await exceptionUserService.UpdateUserPasswordAsync(_user!, "this-is-a-new-password");
+ Assert.Fail($"Expected exception instead of response: {user?.Guid}");
+
+ }
+ else
+ {
+ Assert.Fail($"UserService is null");
+ }
+ }
+ catch (Exception ex)
+ {
+ Assert.IsInstanceOfType(ex, typeof(Exception));
+ }
+ }
+
+ [TestMethod]
+ public async Task UpdateUserRoleAsync_Success()
+ {
+ try
+ {
+ if (_userService != null)
+ {
+ Assert.IsNotNull(_user);
+ Role role = new()
+ {
+ Name = "NewRole",
+ IsNotEditable = false,
+ Guid = Guid.NewGuid().ToString()
+ };
+
+ var oldRole = _user.Role;
+ var user = await _userService.UpdateUserRoleAsync(_user!, role);
+ Assert.IsInstanceOfType(user, typeof(User));
+ Assert.IsNotNull(user);
+ Assert.IsTrue(user.Role?.Id != oldRole?.Id);
+ }
+ else
+ {
+ Assert.Fail($"UserService is null");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex.InnerException);
+ Assert.Fail($"An exception was thrown: {ex}");
+ }
+ }
+
+ [TestMethod]
+ public async Task UpdateUserRoleAsync_Exception()
+ {
+ try
+ {
+ var exceptionUserService = TestUtils.CreateUserServiceException();
+
+ if (exceptionUserService != null)
+ {
+ Assert.IsNotNull(_user);
+ Role role = new()
+ {
+ Name = "NewRole",
+ IsNotEditable = false,
+ Guid = Guid.NewGuid().ToString()
+ };
+ var user = await exceptionUserService.UpdateUserRoleAsync(_user!, role);
+ Assert.Fail($"Expected exception instead of response: {user?.Guid}");
+
+ }
+ else
+ {
+ Assert.Fail($"UserService is null");
+ }
+ }
+ catch (Exception ex)
+ {
+ Assert.IsInstanceOfType(ex, typeof(Exception));
+ }
+ }
+
[TestMethod]
public async Task DeleteUser()
{
diff --git a/MainProject/Controllers/RoleController.cs b/MainProject/Controllers/RoleController.cs
index 5b369a1..ef276d1 100644
--- a/MainProject/Controllers/RoleController.cs
+++ b/MainProject/Controllers/RoleController.cs
@@ -206,7 +206,7 @@ namespace BasicDotnetTemplate.MainProject.Controllers
return NotFound();
}
- await this._roleService.DeleteRoleAsync(role);
+ await this._roleService.DeleteRoleAsync(role);
return Success(String.Empty);
}
diff --git a/MainProject/Controllers/UserController.cs b/MainProject/Controllers/UserController.cs
index cd9502b..0822cf5 100644
--- a/MainProject/Controllers/UserController.cs
+++ b/MainProject/Controllers/UserController.cs
@@ -128,7 +128,187 @@ namespace BasicDotnetTemplate.MainProject.Controllers
}
+ [JwtAuthorization()]
+ [HttpPut("update/{guid}")]
+ [ProducesResponseType(StatusCodes.Status201Created)]
+ [ProducesResponseType>(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType>(StatusCodes.Status500InternalServerError)]
+ public async Task UpdateUserAsync([FromBody] UpdateUserRequest request, string guid)
+ {
+ try
+ {
+ if (!ModelState.IsValid)
+ {
+ return BadRequest(_requestNotWellFormed);
+ }
+ if (request == null || request.Data == null ||
+ String.IsNullOrEmpty(request.Data.FirstName) ||
+ String.IsNullOrEmpty(request.Data.LastName)
+ )
+ {
+ return BadRequest(_requestNotWellFormed);
+ }
+ var user = await this._userService.GetUserByGuidAsync(guid);
+ if(user == null)
+ {
+ return NotFound();
+ }
+
+ user = await this._userService.UpdateUserAsync(request.Data, user);
+
+ var userDto = _mapper?.Map(user);
+
+ return Success(String.Empty, userDto);
+
+ }
+ catch (Exception exception)
+ {
+ var message = this._somethingWentWrong;
+ if (!String.IsNullOrEmpty(exception.Message))
+ {
+ message += $". {exception.Message}";
+ }
+ return InternalServerError(message);
+ }
+
+ }
+
+ [JwtAuthorization()]
+ [HttpPut("update/{guid}/password")]
+ [ProducesResponseType(StatusCodes.Status201Created)]
+ [ProducesResponseType>(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType>(StatusCodes.Status500InternalServerError)]
+ public async Task UpdateUserPasswordAsync(string guid, string newPassword)
+ {
+ try
+ {
+ if (!ModelState.IsValid)
+ {
+ return BadRequest(_requestNotWellFormed);
+ }
+
+ if (String.IsNullOrEmpty(newPassword))
+ {
+ return BadRequest(_requestNotWellFormed);
+ }
+
+ var user = await this._userService.GetUserByGuidAsync(guid);
+ if(user == null)
+ {
+ return NotFound();
+ }
+
+ user = await this._userService.UpdateUserPasswordAsync(user, newPassword);
+
+ var userDto = _mapper?.Map(user);
+
+ return Success(String.Empty, userDto);
+
+ }
+ catch (Exception exception)
+ {
+ var message = this._somethingWentWrong;
+ if (!String.IsNullOrEmpty(exception.Message))
+ {
+ message += $". {exception.Message}";
+ }
+ return InternalServerError(message);
+ }
+
+ }
+
+ [JwtAuthorization()]
+ [HttpPut("update/{guid}/role")]
+ [ProducesResponseType(StatusCodes.Status201Created)]
+ [ProducesResponseType>(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType>(StatusCodes.Status500InternalServerError)]
+ public async Task UpdateUserRoleAsync(string guid, string roleGuid)
+ {
+ try
+ {
+ if (!ModelState.IsValid)
+ {
+ return BadRequest(_requestNotWellFormed);
+ }
+
+ if (String.IsNullOrEmpty(roleGuid))
+ {
+ return BadRequest(_requestNotWellFormed);
+ }
+
+ var role = await this._roleService.GetRoleForUser(roleGuid);
+ if (role == null)
+ {
+ return BadRequest("Role not found");
+ }
+
+ var user = await this._userService.GetUserByGuidAsync(guid);
+ if(user == null)
+ {
+ return NotFound();
+ }
+
+ user = await this._userService.UpdateUserRoleAsync(user, role);
+
+ var userDto = _mapper?.Map(user);
+
+ return Success(String.Empty, userDto);
+
+ }
+ catch (Exception exception)
+ {
+ var message = this._somethingWentWrong;
+ if (!String.IsNullOrEmpty(exception.Message))
+ {
+ message += $". {exception.Message}";
+ }
+ return InternalServerError(message);
+ }
+
+ }
+
+ [JwtAuthorization()]
+ [HttpDelete("{guid}")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType>(StatusCodes.Status404NotFound)]
+ [ProducesResponseType>(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType>(StatusCodes.Status500InternalServerError)]
+ public async Task DeleteUserByGuidAsync(string guid)
+ {
+ try
+ {
+ if (!ModelState.IsValid)
+ {
+ return BadRequest(_requestNotWellFormed);
+ }
+
+ if (String.IsNullOrEmpty(guid))
+ {
+ return BadRequest(_requestNotWellFormed);
+ }
+ var user = await this._userService.GetUserByGuidAsync(guid);
+
+ if (user == null || String.IsNullOrEmpty(user.Guid))
+ {
+ return NotFound();
+ }
+
+ await this._userService.DeleteUserAsync(user);
+
+ return Success(String.Empty);
+ }
+ catch (Exception exception)
+ {
+ var message = this._somethingWentWrong;
+ if (!String.IsNullOrEmpty(exception.Message))
+ {
+ message += $". {exception.Message}";
+ }
+ return InternalServerError(message);
+ }
+
+ }
}
}
\ No newline at end of file
diff --git a/MainProject/MainProject.csproj b/MainProject/MainProject.csproj
index 801b05c..3fecdf7 100644
--- a/MainProject/MainProject.csproj
+++ b/MainProject/MainProject.csproj
@@ -15,21 +15,21 @@
all
-
-
-
-
-
+
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
-
-
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
@@ -37,14 +37,14 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/MainProject/Models/Api/Data/User/CreateUserRequestData.cs b/MainProject/Models/Api/Data/User/CreateUserRequestData.cs
index 2202def..01fb31c 100644
--- a/MainProject/Models/Api/Data/User/CreateUserRequestData.cs
+++ b/MainProject/Models/Api/Data/User/CreateUserRequestData.cs
@@ -1,9 +1,7 @@
namespace BasicDotnetTemplate.MainProject.Models.Api.Data.User;
-public class CreateUserRequestData
+public class CreateUserRequestData : UpdateUserRequestData
{
- public string FirstName { get; set; } = String.Empty;
- public string LastName { get; set; } = String.Empty;
public string Email { get; set; } = String.Empty;
public string Password { get; set; } = String.Empty;
public string? RoleGuid { get; set; }
diff --git a/MainProject/Models/Api/Data/User/UpdateUserRequestData.cs b/MainProject/Models/Api/Data/User/UpdateUserRequestData.cs
new file mode 100644
index 0000000..a5772af
--- /dev/null
+++ b/MainProject/Models/Api/Data/User/UpdateUserRequestData.cs
@@ -0,0 +1,12 @@
+namespace BasicDotnetTemplate.MainProject.Models.Api.Data.User;
+
+public class UpdateUserRequestData
+{
+ public string FirstName { get; set; } = String.Empty;
+ public string LastName { get; set; } = String.Empty;
+
+}
+
+
+
+
diff --git a/MainProject/Models/Api/Request/User/UpdateUserRequest.cs b/MainProject/Models/Api/Request/User/UpdateUserRequest.cs
new file mode 100644
index 0000000..c2bf2df
--- /dev/null
+++ b/MainProject/Models/Api/Request/User/UpdateUserRequest.cs
@@ -0,0 +1,14 @@
+using BasicDotnetTemplate.MainProject.Models.Api.Data.User;
+
+namespace BasicDotnetTemplate.MainProject.Models.Api.Request.User;
+
+public class UpdateUserRequest
+{
+#nullable enable
+ public UpdateUserRequestData? Data { get; set; }
+#nullable disable
+}
+
+
+
+
diff --git a/MainProject/Services/UserService.cs b/MainProject/Services/UserService.cs
index 54ecef4..2f04ce5 100644
--- a/MainProject/Services/UserService.cs
+++ b/MainProject/Services/UserService.cs
@@ -16,7 +16,10 @@ public interface IUserService
Task GetUserByUsernameAndPassword(string email, string password);
Task CheckIfEmailIsValid(string email, string? guid = "");
Task CreateUserAsync(CreateUserRequestData data, Role role);
+ Task UpdateUserAsync(UpdateUserRequestData data, User user);
Task DeleteUserAsync(User user);
+ Task UpdateUserPasswordAsync(User user, string newPassword);
+ Task UpdateUserRoleAsync(User user, Role newRole);
}
public class UserService : BaseService, IUserService
@@ -134,6 +137,31 @@ public class UserService : BaseService, IUserService
return user;
}
+ public async Task UpdateUserAsync(UpdateUserRequestData data, User user)
+ {
+ using var transaction = await _sqlServerContext.Database.BeginTransactionAsync();
+
+ try
+ {
+ user.FirstName = data.FirstName ?? user.FirstName;
+ user.LastName = data.LastName ?? user.LastName;
+ user.UpdateTime = DateTime.UtcNow;
+ user.UpdateUserId = this.GetCurrentUserId();
+
+ _sqlServerContext.Users.Update(user);
+ await _sqlServerContext.SaveChangesAsync();
+ await transaction.CommitAsync();
+ }
+ catch (Exception exception)
+ {
+ Logger.Error(exception, $"[UserService][UpdateUserAsync] | {transaction.TransactionId}");
+ await transaction.RollbackAsync();
+ throw new UpdateException($"An error occurred while updating the user for transaction ID {transaction.TransactionId}.", exception);
+ }
+
+ return user;
+ }
+
public async Task DeleteUserAsync(User user)
{
bool? deleted = false;
@@ -151,6 +179,59 @@ public class UserService : BaseService, IUserService
return deleted;
}
+ public async Task UpdateUserPasswordAsync(User user, string newPassword)
+ {
+ using var transaction = await _sqlServerContext.Database.BeginTransactionAsync();
+ try
+ {
+ var salt = _appSettings.EncryptionSettings?.Salt ?? String.Empty;
+ var pepper = CryptUtils.GeneratePepper();
+ var iterations = _appSettings.EncryptionSettings?.Iterations ?? 10;
+
+ user.PasswordSalt = salt;
+ user.PasswordPepper = pepper;
+ user.PasswordIterations = iterations;
+ user.Password = CryptUtils.GeneratePassword(newPassword, salt, iterations, pepper);
+ user.UpdateTime = DateTime.UtcNow;
+ user.UpdateUserId = this.GetCurrentUserId();
+
+ _sqlServerContext.Users.Update(user);
+ await _sqlServerContext.SaveChangesAsync();
+ await transaction.CommitAsync();
+ }
+ catch (Exception exception)
+ {
+ Logger.Error(exception, $"[UserService][UpdateUserPasswordAsync] | {transaction.TransactionId}");
+ await transaction.RollbackAsync();
+ throw new UpdateException($"An error occurred while updating the user for transaction ID {transaction.TransactionId}.", exception);
+ }
+
+ return user;
+ }
+
+ public async Task UpdateUserRoleAsync(User user, Role newRole)
+ {
+ using var transaction = await _sqlServerContext.Database.BeginTransactionAsync();
+
+ try
+ {
+ user.Role = newRole;
+ user.UpdateTime = DateTime.UtcNow;
+ user.UpdateUserId = this.GetCurrentUserId();
+
+ _sqlServerContext.Users.Update(user);
+ await _sqlServerContext.SaveChangesAsync();
+ await transaction.CommitAsync();
+ }
+ catch (Exception exception)
+ {
+ Logger.Error(exception, $"[UserService][UpdateUserRoleAsync] | {transaction.TransactionId}");
+ await transaction.RollbackAsync();
+ throw new UpdateException($"An error occurred while updating the user for transaction ID {transaction.TransactionId}.", exception);
+ }
+
+ return user;
+ }
}