diff --git a/Directory.Build.props b/Directory.Build.props index 8b8f2ac..b6340c5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,9 +3,16 @@ true none.ignore default + net9.0 + enable + enable + + + CS8618 + \ No newline at end of file diff --git a/PasswordKeeper.BusinessLogic/PasswordKeeper.BusinessLogic.csproj b/PasswordKeeper.BusinessLogic/PasswordKeeper.BusinessLogic.csproj index fc9640d..0b3a384 100644 --- a/PasswordKeeper.BusinessLogic/PasswordKeeper.BusinessLogic.csproj +++ b/PasswordKeeper.BusinessLogic/PasswordKeeper.BusinessLogic.csproj @@ -1,11 +1,5 @@  - - - net9.0 - enable - enable - - + diff --git a/PasswordKeeper.BusinessLogic/Users.cs b/PasswordKeeper.BusinessLogic/Users.cs index b4b6399..80dc979 100644 --- a/PasswordKeeper.BusinessLogic/Users.cs +++ b/PasswordKeeper.BusinessLogic/Users.cs @@ -34,6 +34,18 @@ public async Task UsersExist(bool? admin = null) { return await users.UsersExist(admin); } + + /// + public async Task> GetAllUsers() + { + return await users.GetAllUsers(); + } + + /// + public async Task DeleteUser(long id) + { + await users.DeleteUser(id); + } private const int IterationCount = 1000000; @@ -117,6 +129,7 @@ public async Task Login(string username, string password, byte[] jw PasswordHash = HashPassword(password, ref salt), PasswordSalt = Convert.ToBase64String(salt!), IsAdmin = true, + UserFullName = "Administrator", }; userDto = await users.UpsertUser(userDto); @@ -126,9 +139,13 @@ public async Task Login(string username, string password, byte[] jw return new LoginResult(false, Passwords.CreateMessageString(LoginRejectReason.FailedToCreateAdminUser), false, LoginRejectReason.FailedToCreateAdminUser); } - var token = Passwords.GenerateJwtToken(username, userDto.Id, jwtKey, pseudoDomain); + var token = Passwords.GenerateJwtToken(username, userDto.Id, jwtKey, pseudoDomain, userDto.IsAdmin); return new LoginResult(true, token, false, LoginRejectReason.None); } + else + { + userDto = await users.GetUserByName(username); + } // An existing user, verify the password if (userDto is not null) @@ -136,7 +153,7 @@ public async Task Login(string username, string password, byte[] jw if (Users.VerifyPassword(password, userDto.PasswordHash, Convert.FromBase64String(userDto.PasswordSalt))) { - var token = Passwords.GenerateJwtToken(username, userDto.Id, jwtKey, pseudoDomain); + var token = Passwords.GenerateJwtToken(username, userDto.Id, jwtKey, pseudoDomain, userDto.IsAdmin); return new LoginResult(true, token, false, LoginRejectReason.None); } } diff --git a/PasswordKeeper.Classes/DatabaseUtilities.cs b/PasswordKeeper.Classes/DatabaseUtilities.cs new file mode 100644 index 0000000..762f23d --- /dev/null +++ b/PasswordKeeper.Classes/DatabaseUtilities.cs @@ -0,0 +1,18 @@ +namespace PasswordKeeper.Classes; + +/// +/// Database utilities. +/// +public static class DatabaseUtilities +{ + /// + /// Gets the connection string for a SQLite database. + /// + /// The name of the database. + /// The connection string. + // ReSharper disable once InconsistentNaming (this is how SQLite is written) + public static string GetSQLiteConnectionString(string databaseName) + { + return $"Data Source=./{databaseName}.db;Pooling=False;"; + } +} \ No newline at end of file diff --git a/PasswordKeeper.Classes/PasswordKeeper.Classes.csproj b/PasswordKeeper.Classes/PasswordKeeper.Classes.csproj index 6e17900..cb81b7a 100644 --- a/PasswordKeeper.Classes/PasswordKeeper.Classes.csproj +++ b/PasswordKeeper.Classes/PasswordKeeper.Classes.csproj @@ -1,11 +1,5 @@  - - net9.0 - enable - enable - - diff --git a/PasswordKeeper.Classes/Passwords.cs b/PasswordKeeper.Classes/Passwords.cs index d1992cb..f27cbf9 100644 --- a/PasswordKeeper.Classes/Passwords.cs +++ b/PasswordKeeper.Classes/Passwords.cs @@ -18,14 +18,16 @@ public static class Passwords /// The user ID to generate the JWT token for. /// The JWT key to use for signing the token. /// The pseudo domain to use for the token issuer and audience. + /// Whether the user is an admin or not. /// The JWT token. - public static string GenerateJwtToken(string username, long userId, byte[] jwtKey, string pseudoDomain) + public static string GenerateJwtToken(string username, long userId, byte[] jwtKey, string pseudoDomain, bool isAdmin) { var claims = new[] { new Claim(JwtRegisteredClaimNames.Sub, username), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.NameId, userId.ToString()), + new Claim(ClaimTypes.Role, isAdmin ? "Admin" : "User"), }; var key = new SymmetricSecurityKey(jwtKey); @@ -35,7 +37,11 @@ public static string GenerateJwtToken(string username, long userId, byte[] jwtKe issuer: pseudoDomain, audience: pseudoDomain, claims: claims, + #if DEBUG + expires: DateTime.Now.AddYears(1), + #else expires: DateTime.Now.AddMinutes(30), + #endif signingCredentials: credentials); return new JwtSecurityTokenHandler().WriteToken(token); diff --git a/PasswordKeeper.DAO/PasswordKeeper.DAO.csproj b/PasswordKeeper.DAO/PasswordKeeper.DAO.csproj index c90f988..125f219 100644 --- a/PasswordKeeper.DAO/PasswordKeeper.DAO.csproj +++ b/PasswordKeeper.DAO/PasswordKeeper.DAO.csproj @@ -1,11 +1,5 @@  - - - net9.0 - enable - enable - - + diff --git a/PasswordKeeper.DAO/User.cs b/PasswordKeeper.DAO/User.cs index c736bfe..3ee5790 100644 --- a/PasswordKeeper.DAO/User.cs +++ b/PasswordKeeper.DAO/User.cs @@ -17,6 +17,10 @@ public class User : IUser [MaxLength(255)] public string Username { get; set; } = string.Empty; + /// + [MaxLength(512)] + public string UserFullName { get; set; } = string.Empty; + /// [MaxLength(1000)] public string PasswordHash { get; set; } = string.Empty; diff --git a/PasswordKeeper.DTO/PasswordKeeper.DTO.csproj b/PasswordKeeper.DTO/PasswordKeeper.DTO.csproj index b229e05..9fa87b2 100644 --- a/PasswordKeeper.DTO/PasswordKeeper.DTO.csproj +++ b/PasswordKeeper.DTO/PasswordKeeper.DTO.csproj @@ -1,11 +1,5 @@  - - net9.0 - enable - enable - - diff --git a/PasswordKeeper.DTO/UserDto.cs b/PasswordKeeper.DTO/UserDto.cs index 2610ea6..f96bf94 100644 --- a/PasswordKeeper.DTO/UserDto.cs +++ b/PasswordKeeper.DTO/UserDto.cs @@ -14,6 +14,9 @@ public class UserDto : IUser /// public string Username { get; set; } = string.Empty; + /// + public string UserFullName { get; set; } = string.Empty; + /// [JsonIgnore] public string PasswordHash { get; set; } = string.Empty; diff --git a/PasswordKeeper.DataAccess/PasswordKeeper.DataAccess.csproj b/PasswordKeeper.DataAccess/PasswordKeeper.DataAccess.csproj index 1eb2392..3969f7f 100644 --- a/PasswordKeeper.DataAccess/PasswordKeeper.DataAccess.csproj +++ b/PasswordKeeper.DataAccess/PasswordKeeper.DataAccess.csproj @@ -1,11 +1,5 @@  - - net9.0 - enable - enable - - diff --git a/PasswordKeeper.DataAccess/Users.cs b/PasswordKeeper.DataAccess/Users.cs index 070b324..5e10861 100644 --- a/PasswordKeeper.DataAccess/Users.cs +++ b/PasswordKeeper.DataAccess/Users.cs @@ -83,4 +83,35 @@ public async Task UsersExist(bool? admin = null) return mapper.Map(user); } + + /// + /// Deletes the user with the given ID. + /// + /// The ID of the user to delete. + /// A task representing the asynchronous operation. + public async Task DeleteUser(long id) + { + await using var context = await dbContextFactory.CreateDbContextAsync(); + + var user = await context.Users.FirstOrDefaultAsync(user => user.Id == id); + + if (user != null) + { + context.Users.Remove(user); + await context.SaveChangesAsync(); + } + + // TODO:Delete user data + } + + /// + /// Gets all users. + /// + /// A collection of all users. + public async Task> GetAllUsers() + { + await using var context = await dbContextFactory.CreateDbContextAsync(); + + return mapper.Map>(await context.Users.ToListAsync()); + } } \ No newline at end of file diff --git a/PasswordKeeper.DatabaseMigrations/Migrations/001_InitialMigration.cs b/PasswordKeeper.DatabaseMigrations/Migrations/001_InitialMigration.cs index f578f47..3bdcf92 100644 --- a/PasswordKeeper.DatabaseMigrations/Migrations/001_InitialMigration.cs +++ b/PasswordKeeper.DatabaseMigrations/Migrations/001_InitialMigration.cs @@ -24,6 +24,7 @@ public override void Up() this.Create.Table(nameof(User)).InSchemaIf(Program.DatabaseName, !isSqlite) .WithColumn(nameof(User.Id)).AsInt64().NotNullable().PrimaryKey().Identity() .WithColumn(nameof(User.Username)).AsString(255).NotNullable().Unique() + .WithColumn(nameof(User.UserFullName)).AsString(512).NotNullable() .WithColumn(nameof(User.PasswordHash)).AsString(1000).NotNullable() .WithColumn(nameof(User.PasswordSalt)).AsString(1000).NotNullable() .WithColumn(nameof(User.IsAdmin)).AsBoolean().NotNullable().WithDefaultValue(false); diff --git a/PasswordKeeper.DatabaseMigrations/PasswordKeeper.DatabaseMigrations.csproj b/PasswordKeeper.DatabaseMigrations/PasswordKeeper.DatabaseMigrations.csproj index 7607de5..422a2e4 100644 --- a/PasswordKeeper.DatabaseMigrations/PasswordKeeper.DatabaseMigrations.csproj +++ b/PasswordKeeper.DatabaseMigrations/PasswordKeeper.DatabaseMigrations.csproj @@ -2,9 +2,6 @@ Exe - net9.0 - enable - enable @@ -18,6 +15,7 @@ + diff --git a/PasswordKeeper.DatabaseMigrations/Program.cs b/PasswordKeeper.DatabaseMigrations/Program.cs index 5e8d164..6e9d1e6 100644 --- a/PasswordKeeper.DatabaseMigrations/Program.cs +++ b/PasswordKeeper.DatabaseMigrations/Program.cs @@ -2,6 +2,8 @@ using FluentMigrator.Runner; using Microsoft.Extensions.DependencyInjection; using McMaster.Extensions.CommandLineUtils; +using PasswordKeeper.Classes; + // ReSharper disable MemberCanBePrivate.Global namespace PasswordKeeper.DatabaseMigrations; @@ -51,7 +53,7 @@ public void OnExecute() if (TestDbName != null) { // NOTE: Pooling=False is required for SQLite for the database file to be released after migrations! - connectionString = $"Data Source=./{TestDbName}.db;Pooling=False;"; + connectionString = DatabaseUtilities.GetSQLiteConnectionString(TestDbName); IsTestDb = true; } else diff --git a/PasswordKeeper.Interfaces/IUser.cs b/PasswordKeeper.Interfaces/IUser.cs index f5e7df7..d703a8e 100644 --- a/PasswordKeeper.Interfaces/IUser.cs +++ b/PasswordKeeper.Interfaces/IUser.cs @@ -10,6 +10,11 @@ public interface IUser : IHasId /// string Username { get; set; } + /// + /// The full name of the user. + /// + string UserFullName { get; set; } + /// /// The password of the user. /// diff --git a/PasswordKeeper.Interfaces/PasswordKeeper.Interfaces.csproj b/PasswordKeeper.Interfaces/PasswordKeeper.Interfaces.csproj index 17b910f..c632161 100644 --- a/PasswordKeeper.Interfaces/PasswordKeeper.Interfaces.csproj +++ b/PasswordKeeper.Interfaces/PasswordKeeper.Interfaces.csproj @@ -1,9 +1,3 @@  - - net9.0 - enable - enable - - diff --git a/PasswordKeeper.Server/Controllers/AliveController.cs b/PasswordKeeper.Server/Controllers/AliveController.cs new file mode 100644 index 0000000..a0a9c11 --- /dev/null +++ b/PasswordKeeper.Server/Controllers/AliveController.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace PasswordKeeper.Server.Controllers; + +/// +/// The alive controller. +/// +public class AliveController : ControllerBase +{ + /// + /// Gets the current date and time.S + /// + /// The current date and time. + [Route("")] + [AllowAnonymous] + [HttpGet] + public DateTimeOffset Get() + { + return DateTimeOffset.UtcNow; + } +} \ No newline at end of file diff --git a/PasswordKeeper.Server/Controllers/AuthenticationController.cs b/PasswordKeeper.Server/Controllers/AuthenticationController.cs index d80c528..f387fd4 100644 --- a/PasswordKeeper.Server/Controllers/AuthenticationController.cs +++ b/PasswordKeeper.Server/Controllers/AuthenticationController.cs @@ -1,9 +1,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using PasswordKeeper.BusinessLogic; -using PasswordKeeper.Classes; -using PasswordKeeper.DTO; -using PasswordKeeper.Server.Controllers.Extensions; namespace PasswordKeeper.Server.Controllers; @@ -20,14 +17,6 @@ public class AuthenticationController(Users users) : ControllerBase /// The username for the login. /// The password for the login. public record UserLogin(string Username, string Password); - - /// - /// User change data. - /// - /// The ID of the user to change. - /// The new username for the user. - /// The new password for the user. - public record UserChangeRequest(int UserId, string Username, string Password); /// /// Logs the user in and returns a JWT token. @@ -52,150 +41,24 @@ public async Task Login([FromBody] UserLogin user) return Unauthorized(); } - - - /// - /// Checks a password against the password complexity requirements. - /// - /// The password to check. - /// Ok if the password meets the complexity requirements, otherwise BadRequest with an error message. - [Route(nameof(PasswordOk))] - [HttpPost] - public IActionResult PasswordOk([FromBody] string password) - { - var result = Passwords.IsPasswordOk(password, out var message, out _); - return result ? Ok() : BadRequest(message); - } - - /// - /// Updates the user's password if the requester is authorized and the password meets the complexity requirements. - /// - /// The user change request containing the user ID and new password. - /// - /// Unauthorized if the requester is not the user or admin, BadRequest if the password is invalid or the upsert operation fails, - /// NotFound if the user does not exist, otherwise Ok with the updated user data. - /// - [Route(nameof(UpdateUserPassword))] - [HttpPost] - public async Task UpdateUserPassword([FromBody] UserChangeRequest user) - { - var userDto = await users.GetUserById(user.UserId); - - if (userDto is null) - { - return NotFound(); - } - - if (this.GetLoggedUserId() != user.UserId || !userDto.IsAdmin) - { - return Unauthorized(); - } - - if (!Passwords.IsPasswordOk(user.Password, out var message, out _)) - { - return BadRequest(message); - } - - var salt = Convert.FromBase64String(userDto.PasswordSalt); - userDto.PasswordHash = Users.HashPassword(user.Password, ref salt); - userDto.PasswordSalt = Convert.ToBase64String(salt!); - var result = await users.UpsertUser(userDto); - - return result is null ? BadRequest() : Ok(result); - } /// - /// Updates the user's username if the requester is authorized. - /// - /// The user change request containing the user ID and new username. - /// - /// Unauthorized if the requester is not the user or admin, NotFound if the user does not exist, - /// otherwise BadRequest if the upsert operation fails, or Ok with the updated user data. - /// - public async Task UpdateUserName([FromBody] UserChangeRequest user) - { - var userDto = await users.GetUserById(user.UserId); - - if (userDto is null) - { - return NotFound(); - } - - if (this.GetLoggedUserId() != user.UserId || !userDto.IsAdmin) - { - return Unauthorized(); - } - - if (user.Username.Length < 4) - { - return BadRequest(Passwords.UsernameMustBeAtLeast4CharactersLong); - } - - userDto.Username = user.Username; - var result = await users.UpsertUser(userDto); - - return result is null ? BadRequest() : Ok(result); - } - - /// - /// Creates a new user if the requester is admin. + /// An endpoint for testing unauthorized access. /// - /// The user data to create. - /// - /// Unauthorized if the requester is not admin, BadRequest if the upsert operation fails, - /// otherwise Ok with the created user data. - /// - public async Task CreateUser([FromBody] UserChangeRequest user) + /// An Ok result if the request is authorized. + [Route(nameof(TestUnauthorized))] + public IActionResult TestUnauthorized() { - var loggedUser = await users.GetUserById(this.GetLoggedUserId()); - - if (await users.GetUserByName(user.Username) is not null) - { - return BadRequest(Passwords.UserAlreadyExists); - } - - if (loggedUser == null) - { - return Unauthorized(); - } - - if (loggedUser.IsAdmin) - { - if (!Passwords.IsPasswordOk(user.Password, out var message, out _)) - { - return BadRequest(message); - } - - if (user.Username.Length < 4) - { - return BadRequest(Passwords.UsernameMustBeAtLeast4CharactersLong); - } - - var userDto = new UserDto - { - Username = user.Username, - PasswordHash = string.Empty, - PasswordSalt = string.Empty, - IsAdmin = false, - }; - - var salt = Convert.FromBase64String(userDto.PasswordSalt); - userDto.PasswordHash = Users.HashPassword(user.Password, ref salt); - userDto.PasswordSalt = Convert.ToBase64String(salt!); - var result = await users.UpsertUser(userDto); - - return result is null ? BadRequest() : Ok(result); - } - - return Unauthorized(); + return Ok(); } /// - /// An endpoint for testing unauthorized access. + /// An endpoint for testing admin access. /// /// An Ok result if the request is authorized. - [Route(nameof(TestUnauthorized))] - public IActionResult TestUnauthorized() + [Route(nameof(TestAdminAuthorized))] + [Authorize(Roles = "Admin")] + public IActionResult TestAdminAuthorized() { return Ok(); } diff --git a/PasswordKeeper.Server/Controllers/UsersController.cs b/PasswordKeeper.Server/Controllers/UsersController.cs new file mode 100644 index 0000000..40604d3 --- /dev/null +++ b/PasswordKeeper.Server/Controllers/UsersController.cs @@ -0,0 +1,209 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using PasswordKeeper.BusinessLogic; +using PasswordKeeper.Classes; +using PasswordKeeper.DTO; +using PasswordKeeper.Server.Controllers.Extensions; + +namespace PasswordKeeper.Server.Controllers; + +/// +/// The users controller. +/// +[ApiController] +[Route("api/[controller]")] +public class UsersController(Users users) : ControllerBase +{ + /// + /// User change data. + /// + /// The ID of the user to change. + /// The new username for the user. + /// The new password for the user. + /// The new full name for the user. + public record UserChangeRequest(int UserId, string Username, string Password, string UserFullName); + + /// + /// Creates a new user if the requester is admin. + /// + /// The user data to create. + /// + /// Unauthorized if the requester is not admin, BadRequest if the upsert operation fails, + /// otherwise Ok with the created user data. + /// + [HttpPost] + [Route(nameof(CreateUser))] + [Authorize(Roles = "Admin")] + public async Task CreateUser([FromBody] UserChangeRequest user) + { + var loggedUser = await users.GetUserById(this.GetLoggedUserId()); + + if (await users.GetUserByName(user.Username) is not null) + { + return BadRequest(Passwords.UserAlreadyExists); + } + + if (loggedUser == null) + { + return Unauthorized(); + } + + if (loggedUser.IsAdmin) + { + if (!Passwords.IsPasswordOk(user.Password, out var message, out _)) + { + return BadRequest(message); + } + + if (user.Username.Length < 4) + { + return BadRequest(Passwords.UsernameMustBeAtLeast4CharactersLong); + } + + var userDto = new UserDto + { + Username = user.Username, + PasswordHash = string.Empty, + PasswordSalt = string.Empty, + IsAdmin = false, + UserFullName = user.UserFullName, + }; + + var salt = string.IsNullOrEmpty(userDto.PasswordSalt) ? null : Convert.FromBase64String(userDto.PasswordSalt); + userDto.PasswordHash = Users.HashPassword(user.Password, ref salt); + userDto.PasswordSalt = Convert.ToBase64String(salt!); + var result = await users.UpsertUser(userDto); + + return result is null ? BadRequest() : Ok(result); + } + + return Unauthorized(); + } + + /// + /// Updates the user's password if the requester is authorized and the password meets the complexity requirements. + /// + /// The user change request containing the user ID and new password. + /// + /// Unauthorized if the requester is not the user or admin, BadRequest if the password is invalid or the upsert operation fails, + /// NotFound if the user does not exist, otherwise Ok with the updated user data. + /// + [Route(nameof(UpdateUserPassword))] + [HttpPost] + public async Task UpdateUserPassword([FromBody] UserChangeRequest user) + { + var userDto = await users.GetUserById(user.UserId); + + if (userDto is null) + { + return NotFound(); + } + + if (this.GetLoggedUserId() != user.UserId || !userDto.IsAdmin) + { + return Unauthorized(); + } + + if (!Passwords.IsPasswordOk(user.Password, out var message, out _)) + { + return BadRequest(message); + } + + var salt = Convert.FromBase64String(userDto.PasswordSalt); + userDto.PasswordHash = Users.HashPassword(user.Password, ref salt); + userDto.PasswordSalt = Convert.ToBase64String(salt!); + var result = await users.UpsertUser(userDto); + + return result is null ? BadRequest() : Ok(result); + } + + /// + /// Updates the user's username and full name if the requester is authorized. + /// + /// The user change request containing the user ID, new username and new full name. + /// + /// Unauthorized if the requester is not the user or admin, NotFound if the user does not exist, + /// otherwise BadRequest if the upsert operation fails, or Ok with the updated user data. + /// + public async Task UpdateUserName([FromBody] UserChangeRequest user) + { + var userDto = await users.GetUserById(user.UserId); + + if (userDto is null) + { + return NotFound(); + } + + if (this.GetLoggedUserId() != user.UserId || !userDto.IsAdmin) + { + return Unauthorized(); + } + + if (user.Username.Length < 4) + { + return BadRequest(Passwords.UsernameMustBeAtLeast4CharactersLong); + } + + userDto.Username = user.Username; + userDto.UserFullName = user.UserFullName; + var result = await users.UpsertUser(userDto); + + return result is null ? BadRequest() : Ok(result); + } + + /// + /// Deletes the user with the given ID if the requester is admin. + /// + /// The ID of the user to delete. + /// Unauthorized if the requester is not admin, NotFound if the user does not exist, otherwise Ok. + [Route(nameof(DeleteUser))] + [HttpDelete] + public async Task DeleteUser(int userId) + { + var userDto = await users.GetUserById(userId); + + if (userDto is null) + { + return NotFound(); + } + + var loggedUserId = this.GetLoggedUserId(); + var isLoggedUserAdmin = await users.GetUserById(loggedUserId) is { IsAdmin: true }; + + // Only admins can delete users and admins cannot delete themselves + if (!isLoggedUserAdmin || loggedUserId == userId) + { + return Unauthorized(); + } + + await users.DeleteUser(userId); + + return Ok(); + } + + /// + /// Gets all users. + /// + /// A collection of all users. + [Route(nameof(GetAllUsers))] + [HttpGet] + [Authorize(Roles = "Admin")] + public async Task> GetAllUsers() + { + + return await users.GetAllUsers(); + } + + /// + /// Checks a password against the password complexity requirements. + /// + /// The password to check. + /// Ok if the password meets the complexity requirements, otherwise BadRequest with an error message. + [Route(nameof(PasswordOk))] + [HttpPost] + public IActionResult PasswordOk([FromBody] string password) + { + var result = Passwords.IsPasswordOk(password, out var message, out _); + return result ? Ok() : BadRequest(message); + } +} \ No newline at end of file diff --git a/PasswordKeeper.Server/PasswordKeeper.Server.csproj b/PasswordKeeper.Server/PasswordKeeper.Server.csproj index 88c945b..9009082 100644 --- a/PasswordKeeper.Server/PasswordKeeper.Server.csproj +++ b/PasswordKeeper.Server/PasswordKeeper.Server.csproj @@ -1,9 +1,6 @@ - net9.0 - enable - enable Linux diff --git a/PasswordKeeper.Server/Program.cs b/PasswordKeeper.Server/Program.cs index e90ea18..6ac0750 100644 --- a/PasswordKeeper.Server/Program.cs +++ b/PasswordKeeper.Server/Program.cs @@ -1,4 +1,6 @@ +using System.Security.Claims; using System.Security.Cryptography; +using System.Text.Json; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.EntityFrameworkCore; @@ -42,6 +44,7 @@ public static void Main(string[] args) ValidIssuer = Program.PseudoDomain, ValidAudience = Program.PseudoDomain, IssuerSigningKey = new SymmetricSecurityKey(GetJwtKey()), + RoleClaimType = ClaimTypes.Role, }; }); diff --git a/PasswordKeeper.Tests/ControllerTests.cs b/PasswordKeeper.Tests/ControllerTests.cs index 87eee0c..4dc3d86 100644 --- a/PasswordKeeper.Tests/ControllerTests.cs +++ b/PasswordKeeper.Tests/ControllerTests.cs @@ -60,13 +60,14 @@ public async Task ControllerCreateUserTest() var dbContextFactory = Helpers.GetMockDbContextFactory(nameof(ControllerTests)); var dataAccess = new PasswordKeeper.DataAccess.Users(dbContextFactory, Helpers.CreateMapper()); var businessLogic = new PasswordKeeper.BusinessLogic.Users(dataAccess); - var controller = new AuthenticationController(businessLogic); + var authenticationController = new AuthenticationController(businessLogic); + var usersController = new UsersController(businessLogic); var loginData = new AuthenticationController.UserLogin("firsUserIsAdmin", "Pa1sword%"); - await controller.Login(loginData); + await authenticationController.Login(loginData); - var user = new AuthenticationController.UserChangeRequest(0, "normalUser", "pAssw0rd_"); + var user = new UsersController.UserChangeRequest(0, "normalUser", "pAssw0rd_", "Normal User"); - var createdUser = await controller.CreateUser(user); + var createdUser = await usersController.CreateUser(user); Assert.That(createdUser, Is.TypeOf()); var userDto = ((OkObjectResult)createdUser).Value as UserDto; diff --git a/PasswordKeeper.Tests/Helpers.cs b/PasswordKeeper.Tests/Helpers.cs index e9e6c85..0827126 100644 --- a/PasswordKeeper.Tests/Helpers.cs +++ b/PasswordKeeper.Tests/Helpers.cs @@ -1,6 +1,5 @@ using System.Security.Cryptography; using AutoMapper; -using Microsoft.EntityFrameworkCore; using PasswordKeeper.DAO; using PasswordKeeper.DataAccess; @@ -11,20 +10,6 @@ namespace PasswordKeeper.Tests; /// public static class Helpers { - /// - /// Creates a new SQLite database context. - /// - /// The name of the test class. - /// The database context. - public static Entities GetMemoryContext(string testClassName) - { - var options = new DbContextOptionsBuilder() - .UseSqlite($"Data Source=./{testClassName}.db;Pooling=False;") - .Options; - - return new Entities(options); - } - /// /// Creates a new SQLite database context factory. /// diff --git a/PasswordKeeper.Tests/MockDbContextFactory.cs b/PasswordKeeper.Tests/MockDbContextFactory.cs index 6729fe4..a17e828 100644 --- a/PasswordKeeper.Tests/MockDbContextFactory.cs +++ b/PasswordKeeper.Tests/MockDbContextFactory.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using PasswordKeeper.Classes; using PasswordKeeper.DAO; namespace PasswordKeeper.Tests; @@ -19,7 +20,7 @@ public class MockDbContextFactory(string testClassName) : IDisposableContextFact public Entities CreateDbContext() { var options = new DbContextOptionsBuilder() - .UseSqlite($"Data Source=./{testClassName}.db;Pooling=False;") + .UseSqlite(DatabaseUtilities.GetSQLiteConnectionString(testClassName)) .Options; context = new Entities(options); diff --git a/PasswordKeeper.Tests/PasswordKeeper.Tests.csproj b/PasswordKeeper.Tests/PasswordKeeper.Tests.csproj index 80dc29f..1093705 100644 --- a/PasswordKeeper.Tests/PasswordKeeper.Tests.csproj +++ b/PasswordKeeper.Tests/PasswordKeeper.Tests.csproj @@ -1,10 +1,6 @@ - net9.0 - enable - enable - false true