From 6de30bd77e521ce927ab2f1a32faf763846d47ed Mon Sep 17 00:00:00 2001 From: Oliver Pelly Date: Fri, 27 Feb 2026 16:56:41 -0800 Subject: [PATCH] latest --- api/Program.cs | 1 + api/src/Controllers/GoalController.cs | 0 api/src/Controllers/StudentController.cs | 101 +++++-- .../DataTransferObjects/CreateStudentDto.cs | 2 - .../DataTransferObjects/UpdateStudentDto.cs | 1 - .../Models/DatabaseObjects/dbStudent.cs | 1 + .../Repositories/StudentRepository.cs | 35 ++- api/src/Models/Security/EntityType.cs | 17 ++ api/src/Models/Security/PermissionAction.cs | 15 + api/src/Services/PermissionMatrix.cs | 273 ++++++++++++++++++ api/src/Services/PermissionService.cs | 36 +++ .../procedures/sp_Student_GetByProgram.sql | 18 ++ .../sp_Student_GetByUserAndProgram.sql | 21 ++ db/Objects/procedures/sp_Student_Insert.sql | 16 + db/Objects/procedures/sp_Student_Update.sql | 2 - db/Objects/tables/permission.sql | 9 - db/Objects/tables/role_permission.sql | 10 - 17 files changed, 508 insertions(+), 50 deletions(-) create mode 100644 api/src/Controllers/GoalController.cs create mode 100644 api/src/Models/Security/EntityType.cs create mode 100644 api/src/Models/Security/PermissionAction.cs create mode 100644 api/src/Services/PermissionMatrix.cs create mode 100644 api/src/Services/PermissionService.cs create mode 100644 db/Objects/procedures/sp_Student_GetByProgram.sql create mode 100644 db/Objects/procedures/sp_Student_GetByUserAndProgram.sql delete mode 100644 db/Objects/tables/permission.sql delete mode 100644 db/Objects/tables/role_permission.sql diff --git a/api/Program.cs b/api/Program.cs index cc40a4a..d45abbf 100644 --- a/api/Program.cs +++ b/api/Program.cs @@ -53,6 +53,7 @@ builder.Services.AddAuthentication(options => builder.Services.AddAuthorization(); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddHttpClient(client => { diff --git a/api/src/Controllers/GoalController.cs b/api/src/Controllers/GoalController.cs new file mode 100644 index 0000000..e69de29 diff --git a/api/src/Controllers/StudentController.cs b/api/src/Controllers/StudentController.cs index 1eedef6..fbe88c5 100644 --- a/api/src/Controllers/StudentController.cs +++ b/api/src/Controllers/StudentController.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using WinStudentGoalTracker.Models; using WinStudentGoalTracker.BaseClasses; @@ -12,14 +13,11 @@ public class StudentController : BaseController private readonly StudentRepository _studentRepository = new(); - // TODO refactor this stored procedure - // to getmystudents - // This required auth system to be set up first - [HttpGet] + [HttpGet("my")] + [Authorize(Roles = $"{UserRoles.Teacher},{UserRoles.Paraeducator},{UserRoles.ProgramAdmin}")] [ProducesResponseType(typeof(ResponseResult>), StatusCodes.Status200OK)] - public async Task>>> GetAll() + public async Task>>> GetMyStudents() { - var (userId, email, programId, role, error) = GetProgramUserFromClaims(); if (error is not null) @@ -27,7 +25,34 @@ public class StudentController : BaseController return error; } - var students = await _studentRepository.GetAllAsync(); + var students = await _studentRepository.GetMyStudentsAsync(userId, programId, role); + var response = students.Select(StudentResponse.FromDatabaseModel); + + return Ok(new ResponseResult> + { + Success = true, + Data = response + }); + } + + + // TODO refactor with database changes to ensure + // users who are a district admin are actually associated with a district, and + // then this endpoint should validate that the requested program is part of the district + // Once that is in place, then district admins will be allowed to call this function. + [HttpGet("program/{idProgram:guid}")] + [Authorize(Roles = $"{UserRoles.SuperAdmin}")] + [ProducesResponseType(typeof(ResponseResult>), StatusCodes.Status200OK)] + public async Task>>> GetStudentsForProgram(Guid idProgram) + { + var (userId, email, programId, role, error) = GetProgramUserFromClaims(); + + if (error is not null) + { + return error; + } + + var students = await _studentRepository.GetStudentsByProgramAsync(idProgram); var response = students.Select(StudentResponse.FromDatabaseModel); return Ok(new ResponseResult> @@ -38,12 +63,21 @@ public class StudentController : BaseController } [HttpGet("{idStudent:guid}")] + [Authorize(Roles = $"{UserRoles.Teacher},{UserRoles.Paraeducator},{UserRoles.ProgramAdmin}")] [ProducesResponseType(typeof(ResponseResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ResponseResult), StatusCodes.Status404NotFound)] public async Task>> GetById(Guid idStudent) { - var student = await _studentRepository.GetByIdAsync(idStudent); - if (student is null) + + var (userId, email, programId, role, error) = GetProgramUserFromClaims(); + if (error is not null) + { + return error; + } + + var students = await _studentRepository.GetMyStudentsAsync(userId, programId, role); + + if (!students.Select(s => s.IdStudent).Contains(idStudent)) { return NotFound(new ResponseResult { @@ -51,6 +85,8 @@ public class StudentController : BaseController Message = "Student not found." }); } + + var student = students.Single(s => s.IdStudent == idStudent); return Ok(new ResponseResult { @@ -60,21 +96,20 @@ public class StudentController : BaseController } [HttpPost] + [Authorize(Roles = $"{UserRoles.Teacher}")] [ProducesResponseType(typeof(ResponseResult), StatusCodes.Status201Created)] [ProducesResponseType(typeof(ResponseResult), StatusCodes.Status400BadRequest)] - public async Task>> Create([FromBody] CreateStudentDto request) + public async Task>> Create([FromBody] CreateStudentDto newStudentData) { - var existing = await _studentRepository.GetByIdAsync(request.IdStudent); - if (existing is not null) + var (userId, email, programId, role, error) = GetProgramUserFromClaims(); + + if (error is not null) { - return BadRequest(new ResponseResult - { - Success = false, - Message = $"Student with id {request.IdStudent} already exists." - }); + return error; } - var created = await _studentRepository.InsertAsync(request); + var newStudentId = Guid.NewGuid(); + var created = await _studentRepository.InsertAsync(newStudentData, newStudentId, programId, userId); if (created is null) { return BadRequest(new ResponseResult @@ -93,12 +128,20 @@ public class StudentController : BaseController } [HttpPut("{idStudent:guid}")] + [Authorize(Roles = $"{UserRoles.Teacher}")] [ProducesResponseType(typeof(ResponseResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ResponseResult), StatusCodes.Status404NotFound)] public async Task>> Update(Guid idStudent, [FromBody] UpdateStudentDto request) { - var existing = await _studentRepository.GetByIdAsync(idStudent); - if (existing is null) + var (userId, email, programId, role, error) = GetProgramUserFromClaims(); + if (error is not null) + { + return error; + } + + var students = await _studentRepository.GetMyStudentsAsync(userId, programId, role); + + if (!students.Select(s => s.IdStudent).Contains(idStudent)) { return NotFound(new ResponseResult { @@ -127,10 +170,28 @@ public class StudentController : BaseController } [HttpDelete("{idStudent:guid}")] + [Authorize(Roles = $"{UserRoles.Teacher}")] [ProducesResponseType(typeof(ResponseResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ResponseResult), StatusCodes.Status404NotFound)] public async Task>> Delete(Guid idStudent) { + var (userId, email, programId, role, error) = GetProgramUserFromClaims(); + if (error is not null) + { + return error; + } + + var students = await _studentRepository.GetMyStudentsAsync(userId, programId, role); + + if (!students.Select(s => s.IdStudent).Contains(idStudent)) + { + return NotFound(new ResponseResult + { + Success = false, + Message = "Student not found." + }); + } + var deleted = await _studentRepository.DeleteAsync(idStudent); if (!deleted) { diff --git a/api/src/DataAccess/Models/DataTransferObjects/CreateStudentDto.cs b/api/src/DataAccess/Models/DataTransferObjects/CreateStudentDto.cs index b0e1c19..c4b5e7c 100644 --- a/api/src/DataAccess/Models/DataTransferObjects/CreateStudentDto.cs +++ b/api/src/DataAccess/Models/DataTransferObjects/CreateStudentDto.cs @@ -2,8 +2,6 @@ namespace WinStudentGoalTracker.DataAccess; public class CreateStudentDto { - public required Guid IdStudent { get; set; } - public Guid? IdProgram { get; set; } public string? Identifier { get; set; } public int? ProgramYear { get; set; } public DateTime? EnrollmentDate { get; set; } diff --git a/api/src/DataAccess/Models/DataTransferObjects/UpdateStudentDto.cs b/api/src/DataAccess/Models/DataTransferObjects/UpdateStudentDto.cs index fa721cc..744151b 100644 --- a/api/src/DataAccess/Models/DataTransferObjects/UpdateStudentDto.cs +++ b/api/src/DataAccess/Models/DataTransferObjects/UpdateStudentDto.cs @@ -2,7 +2,6 @@ namespace WinStudentGoalTracker.DataAccess; public class UpdateStudentDto { - public Guid? IdProgram { get; set; } public string? Identifier { get; set; } public int? ProgramYear { get; set; } public DateTime? EnrollmentDate { get; set; } diff --git a/api/src/DataAccess/Models/DatabaseObjects/dbStudent.cs b/api/src/DataAccess/Models/DatabaseObjects/dbStudent.cs index 0f87a9b..f2b5ab9 100644 --- a/api/src/DataAccess/Models/DatabaseObjects/dbStudent.cs +++ b/api/src/DataAccess/Models/DatabaseObjects/dbStudent.cs @@ -4,6 +4,7 @@ public class dbStudent { public required Guid IdStudent { get; set; } public Guid? IdProgram { get; set; } + public Guid PrimaryTeacherId { get; set; } public string? Identifier { get; set; } public int? ProgramYear { get; set; } public DateTime? EnrollmentDate { get; set; } diff --git a/api/src/DataAccess/Repositories/StudentRepository.cs b/api/src/DataAccess/Repositories/StudentRepository.cs index c646958..f1a6e5d 100644 --- a/api/src/DataAccess/Repositories/StudentRepository.cs +++ b/api/src/DataAccess/Repositories/StudentRepository.cs @@ -1,6 +1,7 @@ using System.Data; using Dapper; using MySql.Data.MySqlClient; +using WinStudentGoalTracker.Models; namespace WinStudentGoalTracker.DataAccess; @@ -8,11 +9,33 @@ public class StudentRepository { private IDbConnection Connection => new MySqlConnection(DatabaseManager.ConnectionString); - public async Task> GetAllAsync() + public async Task> GetMyStudentsAsync(Guid userId, Guid programId, string role) + { + return role switch + { + UserRoles.Teacher or UserRoles.ProgramAdmin => + await GetStudentsByProgramAsync(programId), + UserRoles.Paraeducator => + await GetAssignedStudentsAsync(userId, programId), + _ => Enumerable.Empty() + }; + } + + public async Task> GetStudentsByProgramAsync(Guid programId) { using var db = Connection; return await db.QueryAsync( - "sp_Student_GetAll", + "sp_Student_GetByProgram", + new { p_id_program = programId.ToString() }, + commandType: CommandType.StoredProcedure); + } + + private async Task> GetAssignedStudentsAsync(Guid userId, Guid programId) + { + using var db = Connection; + return await db.QueryAsync( + "sp_Student_GetByUserAndProgram", + new { p_id_user = userId.ToString(), p_id_program = programId.ToString() }, commandType: CommandType.StoredProcedure); } @@ -25,15 +48,16 @@ public class StudentRepository commandType: CommandType.StoredProcedure); } - public async Task InsertAsync(CreateStudentDto dto) + public async Task InsertAsync(CreateStudentDto dto, Guid newStudentGuid, Guid programId, Guid userId) { using var db = Connection; return await db.QuerySingleOrDefaultAsync( "sp_Student_Insert", new { - p_id_student = dto.IdStudent.ToString(), - p_id_program = dto.IdProgram?.ToString(), + p_id_student = newStudentGuid.ToString(), + p_id_program = programId, + p_id_user = userId.ToString(), p_identifier = dto.Identifier, p_program_year = dto.ProgramYear, p_enrollment_date = dto.EnrollmentDate, @@ -50,7 +74,6 @@ public class StudentRepository new { p_id_student = idStudent.ToString(), - p_id_program = dto.IdProgram?.ToString(), p_identifier = dto.Identifier, p_program_year = dto.ProgramYear, p_enrollment_date = dto.EnrollmentDate, diff --git a/api/src/Models/Security/EntityType.cs b/api/src/Models/Security/EntityType.cs new file mode 100644 index 0000000..332d1b7 --- /dev/null +++ b/api/src/Models/Security/EntityType.cs @@ -0,0 +1,17 @@ +namespace WinStudentGoalTracker.Models; + +public static class EntityType +{ + public const string SchoolDistrict = "school_district"; + public const string Program = "program"; + public const string User = "user"; + public const string Student = "student"; + public const string Goal = "goal"; + public const string ProgressEvent = "progress_event"; + + public static string? TryParse(string value) => + All.Contains(value) ? value : null; + + public static readonly IReadOnlyList All = + [SchoolDistrict, Program, User, Student, Goal, ProgressEvent]; +} diff --git a/api/src/Models/Security/PermissionAction.cs b/api/src/Models/Security/PermissionAction.cs new file mode 100644 index 0000000..6fa3b8a --- /dev/null +++ b/api/src/Models/Security/PermissionAction.cs @@ -0,0 +1,15 @@ +namespace WinStudentGoalTracker.Models; + +public static class PermissionAction +{ + public const string Create = "create"; + public const string Read = "read"; + public const string Update = "update"; + public const string Delete = "delete"; + + public static string? TryParse(string value) => + All.Contains(value) ? value : null; + + public static readonly IReadOnlyList All = + [Create, Read, Update, Delete]; +} diff --git a/api/src/Services/PermissionMatrix.cs b/api/src/Services/PermissionMatrix.cs new file mode 100644 index 0000000..1d61c6a --- /dev/null +++ b/api/src/Services/PermissionMatrix.cs @@ -0,0 +1,273 @@ +using WinStudentGoalTracker.Models; + +namespace WinStudentGoalTracker.Services; + +public readonly record struct PermissionRule(bool Mine, bool Others); + +public static class PermissionMatrix +{ + private static readonly PermissionRule Allow = new(true, true); + private static readonly PermissionRule MineOnly = new(true, false); + private static readonly PermissionRule Deny = new(false, false); + + // _rules[role][entity][action] → PermissionRule + private static readonly Dictionary>> _rules = new() + { + // ────────────────────────────────────────────── + // Super Admin — full access to everything + // ────────────────────────────────────────────── + [UserRoles.SuperAdmin] = new() + { + [EntityType.SchoolDistrict] = new() + { + [PermissionAction.Create] = Allow, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = Allow, + [PermissionAction.Delete] = Allow, + }, + [EntityType.Program] = new() + { + [PermissionAction.Create] = Allow, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = Allow, + [PermissionAction.Delete] = Allow, + }, + [EntityType.User] = new() + { + [PermissionAction.Create] = Allow, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = Allow, + [PermissionAction.Delete] = Allow, + }, + [EntityType.Student] = new() + { + [PermissionAction.Create] = Allow, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = Allow, + [PermissionAction.Delete] = Allow, + }, + [EntityType.Goal] = new() + { + [PermissionAction.Create] = Allow, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = Allow, + [PermissionAction.Delete] = Allow, + }, + [EntityType.ProgressEvent] = new() + { + [PermissionAction.Create] = Allow, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = Allow, + [PermissionAction.Delete] = Allow, + }, + }, + + // ────────────────────────────────────────────── + // District Admin + // ────────────────────────────────────────────── + [UserRoles.DistrictAdmin] = new() + { + [EntityType.SchoolDistrict] = new() + { + [PermissionAction.Create] = Deny, + [PermissionAction.Read] = MineOnly, + [PermissionAction.Update] = MineOnly, + [PermissionAction.Delete] = Deny, + }, + [EntityType.Program] = new() + { + [PermissionAction.Create] = MineOnly, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = Allow, + [PermissionAction.Delete] = MineOnly, + }, + [EntityType.User] = new() + { + [PermissionAction.Create] = Allow, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = Allow, + [PermissionAction.Delete] = Allow, + }, + [EntityType.Student] = new() + { + [PermissionAction.Create] = Allow, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = Allow, + [PermissionAction.Delete] = Allow, + }, + [EntityType.Goal] = new() + { + [PermissionAction.Create] = Allow, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = Allow, + [PermissionAction.Delete] = Allow, + }, + [EntityType.ProgressEvent] = new() + { + [PermissionAction.Create] = Allow, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = Allow, + [PermissionAction.Delete] = Allow, + }, + }, + + // ────────────────────────────────────────────── + // Program Admin + // ────────────────────────────────────────────── + [UserRoles.ProgramAdmin] = new() + { + [EntityType.SchoolDistrict] = new() + { + [PermissionAction.Create] = Deny, + [PermissionAction.Read] = Deny, + [PermissionAction.Update] = Deny, + [PermissionAction.Delete] = Deny, + }, + [EntityType.Program] = new() + { + [PermissionAction.Create] = Deny, + [PermissionAction.Read] = MineOnly, + [PermissionAction.Update] = MineOnly, + [PermissionAction.Delete] = Deny, + }, + [EntityType.User] = new() + { + [PermissionAction.Create] = MineOnly, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = MineOnly, + [PermissionAction.Delete] = Deny, + }, + [EntityType.Student] = new() + { + [PermissionAction.Create] = Allow, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = Allow, + [PermissionAction.Delete] = MineOnly, + }, + [EntityType.Goal] = new() + { + [PermissionAction.Create] = Allow, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = Allow, + [PermissionAction.Delete] = MineOnly, + }, + [EntityType.ProgressEvent] = new() + { + [PermissionAction.Create] = Allow, + [PermissionAction.Read] = Allow, + [PermissionAction.Update] = Allow, + [PermissionAction.Delete] = MineOnly, + }, + }, + + // ────────────────────────────────────────────── + // Teacher + // ────────────────────────────────────────────── + [UserRoles.Teacher] = new() + { + [EntityType.SchoolDistrict] = new() + { + [PermissionAction.Create] = Deny, + [PermissionAction.Read] = Deny, + [PermissionAction.Update] = Deny, + [PermissionAction.Delete] = Deny, + }, + [EntityType.Program] = new() + { + [PermissionAction.Create] = Deny, + [PermissionAction.Read] = MineOnly, + [PermissionAction.Update] = Deny, + [PermissionAction.Delete] = Deny, + }, + [EntityType.User] = new() + { + [PermissionAction.Create] = Deny, + [PermissionAction.Read] = MineOnly, + [PermissionAction.Update] = MineOnly, + [PermissionAction.Delete] = Deny, + }, + [EntityType.Student] = new() + { + [PermissionAction.Create] = MineOnly, + [PermissionAction.Read] = MineOnly, + [PermissionAction.Update] = MineOnly, + [PermissionAction.Delete] = MineOnly, + }, + [EntityType.Goal] = new() + { + [PermissionAction.Create] = MineOnly, + [PermissionAction.Read] = MineOnly, + [PermissionAction.Update] = MineOnly, + [PermissionAction.Delete] = MineOnly, + }, + [EntityType.ProgressEvent] = new() + { + [PermissionAction.Create] = MineOnly, + [PermissionAction.Read] = MineOnly, + [PermissionAction.Update] = MineOnly, + [PermissionAction.Delete] = MineOnly, + }, + }, + + // ────────────────────────────────────────────── + // Paraeducator + // ────────────────────────────────────────────── + [UserRoles.Paraeducator] = new() + { + [EntityType.SchoolDistrict] = new() + { + [PermissionAction.Create] = Deny, + [PermissionAction.Read] = Deny, + [PermissionAction.Update] = Deny, + [PermissionAction.Delete] = Deny, + }, + [EntityType.Program] = new() + { + [PermissionAction.Create] = Deny, + [PermissionAction.Read] = MineOnly, + [PermissionAction.Update] = Deny, + [PermissionAction.Delete] = Deny, + }, + [EntityType.User] = new() + { + [PermissionAction.Create] = Deny, + [PermissionAction.Read] = MineOnly, + [PermissionAction.Update] = MineOnly, + [PermissionAction.Delete] = Deny, + }, + [EntityType.Student] = new() + { + [PermissionAction.Create] = Deny, + [PermissionAction.Read] = MineOnly, + [PermissionAction.Update] = Deny, + [PermissionAction.Delete] = Deny, + }, + [EntityType.Goal] = new() + { + [PermissionAction.Create] = Deny, + [PermissionAction.Read] = MineOnly, + [PermissionAction.Update] = Deny, + [PermissionAction.Delete] = Deny, + }, + [EntityType.ProgressEvent] = new() + { + [PermissionAction.Create] = MineOnly, + [PermissionAction.Read] = MineOnly, + [PermissionAction.Update] = MineOnly, + [PermissionAction.Delete] = Deny, + }, + }, + }; + + public static PermissionRule? GetRule(string role, string entity, string action) + { + if (_rules.TryGetValue(role, out var entities) + && entities.TryGetValue(entity, out var actions) + && actions.TryGetValue(action, out var rule)) + { + return rule; + } + + return null; + } +} diff --git a/api/src/Services/PermissionService.cs b/api/src/Services/PermissionService.cs new file mode 100644 index 0000000..b8b5e0f --- /dev/null +++ b/api/src/Services/PermissionService.cs @@ -0,0 +1,36 @@ +using WinStudentGoalTracker.Models; + +namespace WinStudentGoalTracker.Services; + +public class PermissionService +{ + /// + /// Checks whether the given role is allowed to perform the specified action + /// on the specified entity, considering ownership. + /// + /// The user's role (use UserRoles constants) + /// The entity being acted on (use EntityType constants) + /// The action being performed (use PermissionAction constants) + /// Whether the resource belongs to the requesting user. + /// For Create actions this parameter is ignored. + /// True if the action is permitted, false otherwise. + public bool IsAllowed(string role, string entity, string action, bool isMine = true) + { + var rule = PermissionMatrix.GetRule(role, entity, action); + + if (rule is null) + { + return false; + } + + + // Create has no ownership concept — use the Mine field as a general "can create" flag + if (action == PermissionAction.Create) + { + return rule.Value.Mine; + } + + + return isMine ? rule.Value.Mine : rule.Value.Others; + } +} diff --git a/db/Objects/procedures/sp_Student_GetByProgram.sql b/db/Objects/procedures/sp_Student_GetByProgram.sql new file mode 100644 index 0000000..2c22fac --- /dev/null +++ b/db/Objects/procedures/sp_Student_GetByProgram.sql @@ -0,0 +1,18 @@ +DELIMITER ;; +CREATE DEFINER=`root`@`%` PROCEDURE `sp_Student_GetByProgram`( + IN p_id_program CHAR(36) +) +BEGIN + SELECT + id_student, + id_program, + identifier, + program_year, + enrollment_date, + expected_grad, + created_at + FROM student + WHERE id_program = p_id_program + ORDER BY id_student; +END;; +DELIMITER ; diff --git a/db/Objects/procedures/sp_Student_GetByUserAndProgram.sql b/db/Objects/procedures/sp_Student_GetByUserAndProgram.sql new file mode 100644 index 0000000..a274ac2 --- /dev/null +++ b/db/Objects/procedures/sp_Student_GetByUserAndProgram.sql @@ -0,0 +1,21 @@ +DELIMITER ;; +CREATE DEFINER=`root`@`%` PROCEDURE `sp_Student_GetByUserAndProgram`( + IN p_id_user CHAR(36), + IN p_id_program CHAR(36) +) +BEGIN + SELECT + s.id_student, + s.id_program, + s.identifier, + s.program_year, + s.enrollment_date, + s.expected_grad, + s.created_at + FROM student s + JOIN user_student us ON us.id_student = s.id_student + WHERE us.id_user = p_id_user + AND s.id_program = p_id_program + ORDER BY s.id_student; +END;; +DELIMITER ; diff --git a/db/Objects/procedures/sp_Student_Insert.sql b/db/Objects/procedures/sp_Student_Insert.sql index 377026a..50a5e65 100644 --- a/db/Objects/procedures/sp_Student_Insert.sql +++ b/db/Objects/procedures/sp_Student_Insert.sql @@ -28,6 +28,22 @@ BEGIN p_expected_grad, UTC_TIMESTAMP() ); + + INSERT INTO user_student + ( + id_user_student, + id_user, + id_student, + is_primary + ) + VALUES + ( + UUID(), + p_id_user, + p_id_student, + 1 + ); + SELECT id_student, id_program, diff --git a/db/Objects/procedures/sp_Student_Update.sql b/db/Objects/procedures/sp_Student_Update.sql index 6cd0f26..6552808 100644 --- a/db/Objects/procedures/sp_Student_Update.sql +++ b/db/Objects/procedures/sp_Student_Update.sql @@ -1,7 +1,6 @@ DELIMITER ;; CREATE DEFINER=`root`@`%` PROCEDURE `sp_Student_Update`( IN p_id_student CHAR(36), - IN p_id_program CHAR(36), IN p_identifier VARCHAR(50), IN p_program_year INT, IN p_enrollment_date DATE, @@ -10,7 +9,6 @@ CREATE DEFINER=`root`@`%` PROCEDURE `sp_Student_Update`( BEGIN UPDATE student SET - id_program = COALESCE(p_id_program, id_program), identifier = COALESCE(p_identifier, identifier), program_year = COALESCE(p_program_year, program_year), enrollment_date = COALESCE(p_enrollment_date, enrollment_date), diff --git a/db/Objects/tables/permission.sql b/db/Objects/tables/permission.sql deleted file mode 100644 index 796520f..0000000 --- a/db/Objects/tables/permission.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE TABLE `permission` ( - `id_permission` char(36) NOT NULL, - `name` varchar(100) DEFAULT NULL, - `description` text, - `resource` varchar(100) DEFAULT NULL, - `action` varchar(50) DEFAULT NULL, - `scope` varchar(50) DEFAULT NULL, - PRIMARY KEY (`id_permission`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; diff --git a/db/Objects/tables/role_permission.sql b/db/Objects/tables/role_permission.sql deleted file mode 100644 index cb8e1fd..0000000 --- a/db/Objects/tables/role_permission.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE `role_permission` ( - `id_role_permission` char(36) NOT NULL, - `id_role` char(36) DEFAULT NULL, - `id_permission` char(36) DEFAULT NULL, - PRIMARY KEY (`id_role_permission`), - KEY `role_permission_ibfk_1` (`id_role`), - KEY `role_permission_ibfk_2` (`id_permission`), - CONSTRAINT `role_permission_ibfk_1` FOREIGN KEY (`id_role`) REFERENCES `role` (`id_role`), - CONSTRAINT `role_permission_ibfk_2` FOREIGN KEY (`id_permission`) REFERENCES `permission` (`id_permission`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;