diff --git a/api/Properties/launchSettings.json b/api/Properties/launchSettings.json index 9c2b617..698411e 100644 --- a/api/Properties/launchSettings.json +++ b/api/Properties/launchSettings.json @@ -1,10 +1,11 @@ -{ +{ "$schema": "https://json.schemastore.org/launchsettings.json", "profiles": { "http": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": false, + "launchBrowser": true, + "launchUrl": "swagger", "applicationUrl": "http://localhost:5123", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/api/src/Controllers/AuthController.cs b/api/src/Controllers/AuthController.cs index 909bebb..de550dc 100644 --- a/api/src/Controllers/AuthController.cs +++ b/api/src/Controllers/AuthController.cs @@ -405,4 +405,51 @@ public class AuthController : BaseController Message = "Logged out successfully." }); } + + // ***************************************************************** + // Sets the password hash and salt for an existing user. + // Accepts a user ID and plaintext password, hashes it, and stores + // the result in the user table. + // ***************************************************************** + [HttpPost("SetPassword")] + [ProducesResponseType(typeof(ResponseResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ResponseResult), StatusCodes.Status400BadRequest)] + public async Task>> SetPassword([FromBody] SetPasswordDto dto) + { + if (string.IsNullOrWhiteSpace(dto.UserId) || string.IsNullOrWhiteSpace(dto.Password)) + { + return BadRequest(new ResponseResult + { + Success = false, + Message = "User ID and password are required." + }); + } + + if (!Guid.TryParse(dto.UserId, out Guid userId)) + { + return BadRequest(new ResponseResult + { + Success = false, + Message = "Invalid user ID format." + }); + } + + var (hash, salt) = PasswordHasher.HashPassword(dto.Password); + var updated = await _userRepo.SetPasswordAsync(userId, hash, salt); + + if (!updated) + { + return Ok(new ResponseResult + { + Success = false, + Message = "User not found." + }); + } + + return Ok(new ResponseResult + { + Success = true, + Message = "Password set successfully." + }); + } } diff --git a/api/src/DataAccess/Models/DataTransferObjects/CreateGoalDto.cs b/api/src/DataAccess/Models/DataTransferObjects/CreateGoalDto.cs index 4dad0a3..4ba2d39 100644 --- a/api/src/DataAccess/Models/DataTransferObjects/CreateGoalDto.cs +++ b/api/src/DataAccess/Models/DataTransferObjects/CreateGoalDto.cs @@ -2,8 +2,8 @@ namespace WinStudentGoalTracker.DataAccess; public class CreateGoalDto { - public string? Title { get; set; } public string? Description { get; set; } public string? Category { get; set; } + public string? Baseline { get; set; } public Guid? GoalParentId { get; set; } } diff --git a/api/src/DataAccess/Models/DataTransferObjects/SetPasswordDto.cs b/api/src/DataAccess/Models/DataTransferObjects/SetPasswordDto.cs new file mode 100644 index 0000000..9331d75 --- /dev/null +++ b/api/src/DataAccess/Models/DataTransferObjects/SetPasswordDto.cs @@ -0,0 +1,7 @@ +namespace WinStudentGoalTracker.DataAccess; + +public class SetPasswordDto +{ + public string? UserId { get; set; } + public string? Password { get; set; } +} diff --git a/api/src/DataAccess/Models/DataTransferObjects/UpdateGoalDto.cs b/api/src/DataAccess/Models/DataTransferObjects/UpdateGoalDto.cs index 8310302..9c76f7c 100644 --- a/api/src/DataAccess/Models/DataTransferObjects/UpdateGoalDto.cs +++ b/api/src/DataAccess/Models/DataTransferObjects/UpdateGoalDto.cs @@ -2,7 +2,7 @@ namespace WinStudentGoalTracker.DataAccess; public class UpdateGoalDto { - public string? Title { get; set; } public string? Description { get; set; } public string? Category { get; set; } + public string? Baseline { get; set; } } diff --git a/api/src/DataAccess/Models/DatabaseObjects/dbStudentBenchmarkRow.cs b/api/src/DataAccess/Models/DatabaseObjects/dbStudentBenchmarkRow.cs index fc95bf8..5860aa2 100644 --- a/api/src/DataAccess/Models/DatabaseObjects/dbStudentBenchmarkRow.cs +++ b/api/src/DataAccess/Models/DatabaseObjects/dbStudentBenchmarkRow.cs @@ -5,7 +5,7 @@ public class dbStudentBenchmarkRow public string? StudentIdentifier { get; set; } public required Guid BenchmarkId { get; set; } public required Guid GoalId { get; set; } - public string? GoalTitle { get; set; } + public string? GoalCategory { get; set; } public string? Benchmark { get; set; } public string? CreatedByName { get; set; } public DateTime CreatedAt { get; set; } diff --git a/api/src/DataAccess/Models/DatabaseObjects/dbStudentGoalRow.cs b/api/src/DataAccess/Models/DatabaseObjects/dbStudentGoalRow.cs index d660976..cb8cfd5 100644 --- a/api/src/DataAccess/Models/DatabaseObjects/dbStudentGoalRow.cs +++ b/api/src/DataAccess/Models/DatabaseObjects/dbStudentGoalRow.cs @@ -5,9 +5,9 @@ public class dbStudentGoalRow public string? StudentIdentifier { get; set; } public required Guid GoalId { get; set; } public Guid? GoalParentId { get; set; } - public string? Title { get; set; } public string? Description { get; set; } public string? Category { get; set; } + public string? Baseline { get; set; } public int ProgressEventCount { get; set; } public int BenchmarkCount { get; set; } } diff --git a/api/src/DataAccess/Repositories/StudentRepository.cs b/api/src/DataAccess/Repositories/StudentRepository.cs index e440017..a5043b1 100644 --- a/api/src/DataAccess/Repositories/StudentRepository.cs +++ b/api/src/DataAccess/Repositories/StudentRepository.cs @@ -144,9 +144,9 @@ public class StudentRepository p_id_goal_parent = dto.GoalParentId?.ToString(), p_id_student = idStudent.ToString(), p_id_user_created = userId.ToString(), - p_title = dto.Title, p_description = dto.Description, - p_category = dto.Category + p_category = dto.Category, + p_baseline = dto.Baseline }, commandType: CommandType.StoredProcedure); @@ -156,9 +156,9 @@ public class StudentRepository { GoalId = newGoalId, GoalParentId = dto.GoalParentId, - Title = dto.Title, Description = dto.Description, Category = dto.Category, + Baseline = dto.Baseline, ProgressEventCount = 0 }; } @@ -191,9 +191,9 @@ public class StudentRepository { GoalId = r.GoalId, GoalParentId = r.GoalParentId, - Title = r.Title, Description = r.Description, Category = r.Category, + Baseline = r.Baseline, ProgressEventCount = r.ProgressEventCount, BenchmarkCount = r.BenchmarkCount }).ToList() @@ -201,7 +201,7 @@ public class StudentRepository } // ***************************************************************** - // Updates a goal's title, description, and category. + // Updates a goal's description, category, and baseline. // ***************************************************************** public async Task UpdateGoalAsync(Guid goalId, UpdateGoalDto dto) { @@ -214,9 +214,9 @@ public class StudentRepository p_id_goal_parent = (string?)null, p_id_student = (string?)null, p_id_user_created = (string?)null, - p_title = dto.Title, p_description = dto.Description, - p_category = dto.Category + p_category = dto.Category, + p_baseline = dto.Baseline }, commandType: CommandType.StoredProcedure); return rowsAffected > 0; @@ -254,7 +254,7 @@ public class StudentRepository { BenchmarkId = r.BenchmarkId, GoalId = r.GoalId, - GoalTitle = r.GoalTitle, + GoalCategory = r.GoalCategory, Benchmark = r.Benchmark, CreatedByName = r.CreatedByName, CreatedAt = r.CreatedAt, diff --git a/api/src/DataAccess/Repositories/UserRepository.cs b/api/src/DataAccess/Repositories/UserRepository.cs index 0ae96f2..ce210be 100644 --- a/api/src/DataAccess/Repositories/UserRepository.cs +++ b/api/src/DataAccess/Repositories/UserRepository.cs @@ -43,4 +43,23 @@ public class UserRepository new { p_id_user = idUser.ToString() }, commandType: CommandType.StoredProcedure); } + + // ***************************************************************** + // Updates the password hash and salt for the given user. + // Returns true if the user was found and updated. + // ***************************************************************** + public async Task SetPasswordAsync(Guid userId, string passwordHash, string passwordSalt) + { + using var db = Connection; + var rowsAffected = await db.QuerySingleOrDefaultAsync( + "sp_User_SetPassword", + new + { + p_id_user = userId.ToString(), + p_password_hash = passwordHash, + p_password_salt = passwordSalt + }, + commandType: CommandType.StoredProcedure); + return rowsAffected > 0; + } } diff --git a/api/src/Models/ResponseTypes/StudentBenchmarkItem.cs b/api/src/Models/ResponseTypes/StudentBenchmarkItem.cs index b1ab69e..c6cc3b9 100644 --- a/api/src/Models/ResponseTypes/StudentBenchmarkItem.cs +++ b/api/src/Models/ResponseTypes/StudentBenchmarkItem.cs @@ -4,7 +4,7 @@ public class StudentBenchmarkItem { public Guid BenchmarkId { get; set; } public Guid GoalId { get; set; } - public string? GoalTitle { get; set; } + public string? GoalCategory { get; set; } public string? Benchmark { get; set; } public string? CreatedByName { get; set; } public DateTime CreatedAt { get; set; } diff --git a/api/src/Models/ResponseTypes/StudentGoalItem.cs b/api/src/Models/ResponseTypes/StudentGoalItem.cs index 6e39936..26acab4 100644 --- a/api/src/Models/ResponseTypes/StudentGoalItem.cs +++ b/api/src/Models/ResponseTypes/StudentGoalItem.cs @@ -4,9 +4,9 @@ public class StudentGoalItem { public Guid GoalId { get; set; } public Guid? GoalParentId { get; set; } - public string? Title { get; set; } public string? Description { get; set; } public string? Category { get; set; } + public string? Baseline { get; set; } public int ProgressEventCount { get; set; } public int BenchmarkCount { get; set; } } diff --git a/db/Objects/procedures/sp_Benchmark_GetByStudentId.sql b/db/Objects/procedures/sp_Benchmark_GetByStudentId.sql index 0ff1ce2..222266b 100644 --- a/db/Objects/procedures/sp_Benchmark_GetByStudentId.sql +++ b/db/Objects/procedures/sp_Benchmark_GetByStudentId.sql @@ -5,7 +5,7 @@ BEGIN s.`identifier` AS `studentIdentifier`, b.`id_benchmark` AS `benchmarkId`, b.`id_goal` AS `goalId`, - g.`title` AS `goalTitle`, + g.`category` AS `goalCategory`, b.`benchmark` AS `benchmark`, u.`name` AS `createdByName`, b.`created_at` AS `createdAt`, diff --git a/db/Objects/procedures/sp_Goal_GetAll.sql b/db/Objects/procedures/sp_Goal_GetAll.sql index 357163d..04f2ba3 100644 --- a/db/Objects/procedures/sp_Goal_GetAll.sql +++ b/db/Objects/procedures/sp_Goal_GetAll.sql @@ -5,9 +5,9 @@ BEGIN goalId, goalParentId, studentId, - title, description, category, + baseline, progressEventCount FROM v_goal_card ORDER BY goalId; diff --git a/db/Objects/procedures/sp_Goal_GetById.sql b/db/Objects/procedures/sp_Goal_GetById.sql index 1eaae5d..362c2ff 100644 --- a/db/Objects/procedures/sp_Goal_GetById.sql +++ b/db/Objects/procedures/sp_Goal_GetById.sql @@ -5,9 +5,9 @@ BEGIN goalId, goalParentId, studentId, - title, description, category, + baseline, progressEventCount FROM v_goal_card WHERE goalId = p_id_goal diff --git a/db/Objects/procedures/sp_Goal_GetByStudentId.sql b/db/Objects/procedures/sp_Goal_GetByStudentId.sql index 13b9132..3428425 100644 --- a/db/Objects/procedures/sp_Goal_GetByStudentId.sql +++ b/db/Objects/procedures/sp_Goal_GetByStudentId.sql @@ -5,9 +5,9 @@ BEGIN s.`identifier` AS `studentIdentifier`, vc.`goalId`, vc.`goalParentId`, - vc.`title`, vc.`description`, vc.`category`, + vc.`baseline`, vc.`progressEventCount`, vc.`benchmarkCount` FROM `v_goal_card` vc diff --git a/db/Objects/procedures/sp_Goal_Insert.sql b/db/Objects/procedures/sp_Goal_Insert.sql index f7638de..092c1c4 100644 --- a/db/Objects/procedures/sp_Goal_Insert.sql +++ b/db/Objects/procedures/sp_Goal_Insert.sql @@ -4,9 +4,9 @@ CREATE DEFINER=`root`@`%` PROCEDURE `sp_Goal_Insert`( IN p_id_goal_parent CHAR(36), IN p_id_student CHAR(36), IN p_id_user_created CHAR(36), - IN p_title VARCHAR(255), IN p_description TEXT, - IN p_category VARCHAR(100) + IN p_category VARCHAR(255), + IN p_baseline TEXT ) BEGIN INSERT INTO goal @@ -15,9 +15,9 @@ BEGIN id_goal_parent, id_student, id_user_created, - title, description, category, + baseline, created_at, updated_at ) @@ -27,9 +27,9 @@ BEGIN p_id_goal_parent, p_id_student, p_id_user_created, - p_title, p_description, p_category, + p_baseline, UTC_TIMESTAMP(), UTC_TIMESTAMP() ); @@ -38,9 +38,9 @@ BEGIN id_goal_parent, id_student, id_user_created, - title, description, category, + baseline, created_at, updated_at FROM goal diff --git a/db/Objects/procedures/sp_Goal_Update.sql b/db/Objects/procedures/sp_Goal_Update.sql index c08da01..7825217 100644 --- a/db/Objects/procedures/sp_Goal_Update.sql +++ b/db/Objects/procedures/sp_Goal_Update.sql @@ -4,9 +4,9 @@ CREATE DEFINER=`root`@`%` PROCEDURE `sp_Goal_Update`( IN p_id_goal_parent CHAR(36), IN p_id_student CHAR(36), IN p_id_user_created CHAR(36), - IN p_title VARCHAR(255), IN p_description TEXT, - IN p_category VARCHAR(100) + IN p_category VARCHAR(255), + IN p_baseline TEXT ) BEGIN UPDATE goal @@ -14,9 +14,9 @@ BEGIN id_goal_parent = COALESCE(p_id_goal_parent, id_goal_parent), id_student = COALESCE(p_id_student, id_student), id_user_created = COALESCE(p_id_user_created, id_user_created), - title = COALESCE(p_title, title), description = COALESCE(p_description, description), category = COALESCE(p_category, category), + baseline = COALESCE(p_baseline, baseline), updated_at = UTC_TIMESTAMP() WHERE id_goal = p_id_goal; SELECT ROW_COUNT() AS rows_affected; diff --git a/db/Objects/procedures/sp_User_SetPassword.sql b/db/Objects/procedures/sp_User_SetPassword.sql new file mode 100644 index 0000000..8d1991c --- /dev/null +++ b/db/Objects/procedures/sp_User_SetPassword.sql @@ -0,0 +1,15 @@ +DELIMITER ;; +CREATE DEFINER=`root`@`%` PROCEDURE `sp_User_SetPassword`( + IN p_id_user CHAR(36), + IN p_password_hash VARCHAR(255), + IN p_password_salt VARCHAR(255) +) +BEGIN + UPDATE user + SET password_hash = p_password_hash, + password_salt = p_password_salt, + password_updated_at = UTC_TIMESTAMP() + WHERE id_user = p_id_user; + SELECT ROW_COUNT() AS rows_affected; +END;; +DELIMITER ; diff --git a/db/Objects/tables/goal.sql b/db/Objects/tables/goal.sql index 7fa149a..9f8c3e5 100644 --- a/db/Objects/tables/goal.sql +++ b/db/Objects/tables/goal.sql @@ -3,9 +3,9 @@ CREATE TABLE `goal` ( `id_goal_parent` char(36) DEFAULT NULL, `id_student` char(36) DEFAULT NULL, `id_user_created` char(36) DEFAULT NULL, - `title` varchar(255) DEFAULT NULL, `description` text, - `category` varchar(100) DEFAULT NULL, + `category` varchar(255) DEFAULT NULL, + `baseline` text, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id_goal`), diff --git a/db/Objects/views/v_goal_card.sql b/db/Objects/views/v_goal_card.sql index a935665..7835cac 100644 --- a/db/Objects/views/v_goal_card.sql +++ b/db/Objects/views/v_goal_card.sql @@ -1,7 +1,7 @@ CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`%` SQL SECURITY DEFINER VIEW `winstudentgoaltracker`.`v_goal_card` AS -select `g`.`id_goal` AS `goalId`,`g`.`id_goal_parent` AS `goalParentId`,`g`.`id_student` AS `studentId`,`g`.`title` AS `title`,`g`.`description` AS `description`,`g`.`category` AS `category`,count(distinct `pe`.`id_progress_event`) AS `progressEventCount`,count(distinct `b`.`id_benchmark`) AS `benchmarkCount` +select `g`.`id_goal` AS `goalId`,`g`.`id_goal_parent` AS `goalParentId`,`g`.`id_student` AS `studentId`,`g`.`description` AS `description`,`g`.`category` AS `category`,`g`.`baseline` AS `baseline`,count(distinct `pe`.`id_progress_event`) AS `progressEventCount`,count(distinct `b`.`id_benchmark`) AS `benchmarkCount` from ((`winstudentgoaltracker`.`goal` `g` left join `winstudentgoaltracker`.`progress_event` `pe` on((`pe`.`id_goal` = `g`.`id_goal`))) left -join `winstudentgoaltracker`.`benchmark` `b` on((`b`.`id_goal` = `g`.`id_goal`))) group by `g`.`id_goal`,`g`.`id_goal_parent`,`g`.`id_student`,`g`.`title`,`g`.`description`,`g`.`category`; +join `winstudentgoaltracker`.`benchmark` `b` on((`b`.`id_goal` = `g`.`id_goal`))) group by `g`.`id_goal`,`g`.`id_goal_parent`,`g`.`id_student`,`g`.`description`,`g`.`category`,`g`.`baseline`; diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/add-goal-modal/add-goal-modal.html b/ui/winstudentgoaltracker/src/app/desktop/components/add-goal-modal/add-goal-modal.html index 22b4906..a071034 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/add-goal-modal/add-goal-modal.html +++ b/ui/winstudentgoaltracker/src/app/desktop/components/add-goal-modal/add-goal-modal.html @@ -8,18 +8,6 @@