moved database primary keys to guids

This commit is contained in:
2026-02-20 20:24:57 -08:00
parent b7a78b7bb9
commit da4320f26e
14 changed files with 58 additions and 50 deletions
+3 -3
View File
@@ -5,14 +5,14 @@ namespace WinStudentGoalTracker.BaseClasses;
public class BaseController : ControllerBase public class BaseController : ControllerBase
{ {
protected (int userId, ActionResult? error) GetUserIdFromClaims() protected (Guid userId, ActionResult? error) GetUserIdFromClaims()
{ {
var userIdClaim = User.FindFirst("user_id")?.Value var userIdClaim = User.FindFirst("user_id")?.Value
?? User.FindFirst(ClaimTypes.NameIdentifier)?.Value; ?? User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrWhiteSpace(userIdClaim) || !int.TryParse(userIdClaim, out var userId)) if (string.IsNullOrWhiteSpace(userIdClaim) || !Guid.TryParse(userIdClaim, out var userId))
{ {
return (0, Unauthorized("Missing or invalid user_id claim.")); return (Guid.Empty, Unauthorized("Missing or invalid user_id claim."));
} }
return (userId, null); return (userId, null);
+4 -2
View File
@@ -87,6 +87,7 @@ public class AuthController : BaseController
// Store refresh token in database (30 days expiration) // Store refresh token in database (30 days expiration)
var refreshTokenId = await _authRepo.CreateRefreshTokenAsync( var refreshTokenId = await _authRepo.CreateRefreshTokenAsync(
Guid.NewGuid(),
user.IdUser, user.IdUser,
refreshTokenHash, refreshTokenHash,
refreshTokenSalt, refreshTokenSalt,
@@ -151,7 +152,7 @@ public class AuthController : BaseController
var tokenIdStr = refreshTokenDto.RefreshToken[..dotIndex]; var tokenIdStr = refreshTokenDto.RefreshToken[..dotIndex];
var secretToken = refreshTokenDto.RefreshToken[(dotIndex + 1)..]; var secretToken = refreshTokenDto.RefreshToken[(dotIndex + 1)..];
if (!int.TryParse(tokenIdStr, out int tokenId)) if (!Guid.TryParse(tokenIdStr, out Guid tokenId))
{ {
return BadRequest(new ResponseResult<TokenRefreshResponse> return BadRequest(new ResponseResult<TokenRefreshResponse>
{ {
@@ -221,6 +222,7 @@ public class AuthController : BaseController
var newRefreshTokenId = await _authRepo.ReplaceRefreshTokenAsync( var newRefreshTokenId = await _authRepo.ReplaceRefreshTokenAsync(
matchedToken.IdRefreshToken, matchedToken.IdRefreshToken,
Guid.NewGuid(),
tokenUser.IdUser, tokenUser.IdUser,
newRefreshTokenHash, newRefreshTokenHash,
newRefreshTokenSalt, newRefreshTokenSalt,
@@ -272,7 +274,7 @@ public class AuthController : BaseController
if (error != null) return error; if (error != null) return error;
var dotIndex = logoutDto.RefreshToken.IndexOf('.'); var dotIndex = logoutDto.RefreshToken.IndexOf('.');
if (dotIndex < 1 || !int.TryParse(logoutDto.RefreshToken[..dotIndex], out int tokenId)) if (dotIndex < 1 || !Guid.TryParse(logoutDto.RefreshToken[..dotIndex], out Guid tokenId))
{ {
return BadRequest(new ResponseResult<object> return BadRequest(new ResponseResult<object>
{ {
+6 -6
View File
@@ -29,10 +29,10 @@ public class StudentController : BaseController
}); });
} }
[HttpGet("{idStudent:int}")] [HttpGet("{idStudent:guid}")]
[ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status404NotFound)]
public async Task<ActionResult<ResponseResult<StudentResponse>>> GetById(int idStudent) public async Task<ActionResult<ResponseResult<StudentResponse>>> GetById(Guid idStudent)
{ {
var student = await _studentRepository.GetByIdAsync(idStudent); var student = await _studentRepository.GetByIdAsync(idStudent);
if (student is null) if (student is null)
@@ -84,10 +84,10 @@ public class StudentController : BaseController
}); });
} }
[HttpPut("{idStudent:int}")] [HttpPut("{idStudent:guid}")]
[ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status404NotFound)]
public async Task<ActionResult<ResponseResult<StudentResponse>>> Update(int idStudent, [FromBody] UpdateStudentDto request) public async Task<ActionResult<ResponseResult<StudentResponse>>> Update(Guid idStudent, [FromBody] UpdateStudentDto request)
{ {
var existing = await _studentRepository.GetByIdAsync(idStudent); var existing = await _studentRepository.GetByIdAsync(idStudent);
if (existing is null) if (existing is null)
@@ -118,10 +118,10 @@ public class StudentController : BaseController
}); });
} }
[HttpDelete("{idStudent:int}")] [HttpDelete("{idStudent:guid}")]
[ProducesResponseType(typeof(ResponseResult<object>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ResponseResult<object>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ResponseResult<object>), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ResponseResult<object>), StatusCodes.Status404NotFound)]
public async Task<ActionResult<ResponseResult<object>>> Delete(int idStudent) public async Task<ActionResult<ResponseResult<object>>> Delete(Guid idStudent)
{ {
var deleted = await _studentRepository.DeleteAsync(idStudent); var deleted = await _studentRepository.DeleteAsync(idStudent);
if (!deleted) if (!deleted)
@@ -2,8 +2,8 @@ namespace WinStudentGoalTracker.DataAccess;
public class CreateStudentDto public class CreateStudentDto
{ {
public required int IdStudent { get; set; } public required Guid IdStudent { get; set; }
public int? IdProgram { get; set; } public Guid? IdProgram { get; set; }
public string? Identifier { get; set; } public string? Identifier { get; set; }
public int? ProgramYear { get; set; } public int? ProgramYear { get; set; }
public DateTime? EnrollmentDate { get; set; } public DateTime? EnrollmentDate { get; set; }
@@ -2,7 +2,7 @@ namespace WinStudentGoalTracker.DataAccess;
public class UpdateStudentDto public class UpdateStudentDto
{ {
public int? IdProgram { get; set; } public Guid? IdProgram { get; set; }
public string? Identifier { get; set; } public string? Identifier { get; set; }
public int? ProgramYear { get; set; } public int? ProgramYear { get; set; }
public DateTime? EnrollmentDate { get; set; } public DateTime? EnrollmentDate { get; set; }
@@ -2,8 +2,8 @@ namespace WinStudentGoalTracker.DataAccess;
public class dbRefreshToken public class dbRefreshToken
{ {
public int IdRefreshToken { get; set; } public Guid IdRefreshToken { get; set; }
public int IdUser { get; set; } public Guid IdUser { get; set; }
public required string TokenHash { get; set; } public required string TokenHash { get; set; }
public required string TokenSalt { get; set; } public required string TokenSalt { get; set; }
public DateTime ExpiresAt { get; set; } public DateTime ExpiresAt { get; set; }
@@ -11,7 +11,7 @@ public class dbRefreshToken
public DateTime? RevokedAt { get; set; } public DateTime? RevokedAt { get; set; }
public string? DeviceInfo { get; set; } public string? DeviceInfo { get; set; }
public string? UserAgent { get; set; } public string? UserAgent { get; set; }
public int? ReplacedByTokenId { get; set; } public Guid? ReplacedByTokenId { get; set; }
public DateTime CreatedAt { get; set; } public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; } public DateTime UpdatedAt { get; set; }
} }
@@ -2,8 +2,8 @@ namespace WinStudentGoalTracker.DataAccess;
public class dbStudent public class dbStudent
{ {
public required int IdStudent { get; set; } public required Guid IdStudent { get; set; }
public int? IdProgram { get; set; } public Guid? IdProgram { get; set; }
public string? Identifier { get; set; } public string? Identifier { get; set; }
public int? ProgramYear { get; set; } public int? ProgramYear { get; set; }
public DateTime? EnrollmentDate { get; set; } public DateTime? EnrollmentDate { get; set; }
@@ -2,8 +2,8 @@ namespace WinStudentGoalTracker.DataAccess;
public class dbUser public class dbUser
{ {
public required int IdUser { get; set; } public required Guid IdUser { get; set; }
public int? IdRole { get; set; } public Guid? IdRole { get; set; }
public string? Email { get; set; } public string? Email { get; set; }
public string? Name { get; set; } public string? Name { get; set; }
public string? PasswordHash { get; set; } public string? PasswordHash { get; set; }
@@ -8,8 +8,9 @@ public class AuthRepository
{ {
private IDbConnection Connection => new MySqlConnection(DatabaseManager.ConnectionString); private IDbConnection Connection => new MySqlConnection(DatabaseManager.ConnectionString);
public async Task<int?> CreateRefreshTokenAsync( public async Task<Guid?> CreateRefreshTokenAsync(
int userId, Guid refreshTokenId,
Guid userId,
string tokenHash, string tokenHash,
string tokenSalt, string tokenSalt,
int expiresInSeconds, int expiresInSeconds,
@@ -17,11 +18,12 @@ public class AuthRepository
string? userAgent) string? userAgent)
{ {
using var db = Connection; using var db = Connection;
return await db.QuerySingleOrDefaultAsync<int?>( var result = await db.QuerySingleOrDefaultAsync<string?>(
"sp_RefreshToken_Create", "sp_RefreshToken_Create",
new new
{ {
p_id_user = userId, p_id_refresh_token = refreshTokenId.ToString(),
p_id_user = userId.ToString(),
p_token_hash = tokenHash, p_token_hash = tokenHash,
p_token_salt = tokenSalt, p_token_salt = tokenSalt,
p_expires_in_seconds = expiresInSeconds, p_expires_in_seconds = expiresInSeconds,
@@ -29,30 +31,32 @@ public class AuthRepository
p_user_agent = userAgent p_user_agent = userAgent
}, },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
return result != null ? Guid.Parse(result) : null;
} }
public async Task<dbRefreshToken?> GetRefreshTokenByIdAsync(int refreshTokenId) public async Task<dbRefreshToken?> GetRefreshTokenByIdAsync(Guid refreshTokenId)
{ {
using var db = Connection; using var db = Connection;
return await db.QuerySingleOrDefaultAsync<dbRefreshToken>( return await db.QuerySingleOrDefaultAsync<dbRefreshToken>(
"sp_RefreshToken_GetById", "sp_RefreshToken_GetById",
new { p_id_refresh_token = refreshTokenId }, new { p_id_refresh_token = refreshTokenId.ToString() },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
} }
public async Task<bool> RevokeRefreshTokenAsync(int refreshTokenId) public async Task<bool> RevokeRefreshTokenAsync(Guid refreshTokenId)
{ {
using var db = Connection; using var db = Connection;
var rowsAffected = await db.QuerySingleOrDefaultAsync<int>( var rowsAffected = await db.QuerySingleOrDefaultAsync<int>(
"sp_RefreshToken_Revoke", "sp_RefreshToken_Revoke",
new { p_id_refresh_token = refreshTokenId }, new { p_id_refresh_token = refreshTokenId.ToString() },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
return rowsAffected > 0; return rowsAffected > 0;
} }
public async Task<int?> ReplaceRefreshTokenAsync( public async Task<Guid?> ReplaceRefreshTokenAsync(
int oldTokenId, Guid oldTokenId,
int userId, Guid newTokenId,
Guid userId,
string tokenHash, string tokenHash,
string tokenSalt, string tokenSalt,
int expiresInSeconds, int expiresInSeconds,
@@ -60,12 +64,13 @@ public class AuthRepository
string? userAgent) string? userAgent)
{ {
using var db = Connection; using var db = Connection;
return await db.QuerySingleOrDefaultAsync<int?>( var result = await db.QuerySingleOrDefaultAsync<string?>(
"sp_RefreshToken_Replace", "sp_RefreshToken_Replace",
new new
{ {
p_old_token_id = oldTokenId, p_old_token_id = oldTokenId.ToString(),
p_id_user = userId, p_id_refresh_token = newTokenId.ToString(),
p_id_user = userId.ToString(),
p_token_hash = tokenHash, p_token_hash = tokenHash,
p_token_salt = tokenSalt, p_token_salt = tokenSalt,
p_expires_in_seconds = expiresInSeconds, p_expires_in_seconds = expiresInSeconds,
@@ -73,5 +78,6 @@ public class AuthRepository
p_user_agent = userAgent p_user_agent = userAgent
}, },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
return result != null ? Guid.Parse(result) : null;
} }
} }
@@ -16,12 +16,12 @@ public class StudentRepository
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
} }
public async Task<dbStudent?> GetByIdAsync(int idStudent) public async Task<dbStudent?> GetByIdAsync(Guid idStudent)
{ {
using var db = Connection; using var db = Connection;
return await db.QuerySingleOrDefaultAsync<dbStudent>( return await db.QuerySingleOrDefaultAsync<dbStudent>(
"sp_Student_GetById", "sp_Student_GetById",
new { p_id_student = idStudent }, new { p_id_student = idStudent.ToString() },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
} }
@@ -32,8 +32,8 @@ public class StudentRepository
"sp_Student_Insert", "sp_Student_Insert",
new new
{ {
p_id_student = dto.IdStudent, p_id_student = dto.IdStudent.ToString(),
p_id_program = dto.IdProgram, p_id_program = dto.IdProgram?.ToString(),
p_identifier = dto.Identifier, p_identifier = dto.Identifier,
p_program_year = dto.ProgramYear, p_program_year = dto.ProgramYear,
p_enrollment_date = dto.EnrollmentDate, p_enrollment_date = dto.EnrollmentDate,
@@ -42,15 +42,15 @@ public class StudentRepository
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
} }
public async Task<bool> UpdateAsync(int idStudent, UpdateStudentDto dto) public async Task<bool> UpdateAsync(Guid idStudent, UpdateStudentDto dto)
{ {
using var db = Connection; using var db = Connection;
var rowsAffected = await db.ExecuteScalarAsync<int>( var rowsAffected = await db.ExecuteScalarAsync<int>(
"sp_Student_Update", "sp_Student_Update",
new new
{ {
p_id_student = idStudent, p_id_student = idStudent.ToString(),
p_id_program = dto.IdProgram, p_id_program = dto.IdProgram?.ToString(),
p_identifier = dto.Identifier, p_identifier = dto.Identifier,
p_program_year = dto.ProgramYear, p_program_year = dto.ProgramYear,
p_enrollment_date = dto.EnrollmentDate, p_enrollment_date = dto.EnrollmentDate,
@@ -60,12 +60,12 @@ public class StudentRepository
return rowsAffected > 0; return rowsAffected > 0;
} }
public async Task<bool> DeleteAsync(int idStudent) public async Task<bool> DeleteAsync(Guid idStudent)
{ {
using var db = Connection; using var db = Connection;
var rowsAffected = await db.ExecuteScalarAsync<int>( var rowsAffected = await db.ExecuteScalarAsync<int>(
"sp_Student_Delete", "sp_Student_Delete",
new { p_id_student = idStudent }, new { p_id_student = idStudent.ToString() },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
return rowsAffected > 0; return rowsAffected > 0;
} }
@@ -17,12 +17,12 @@ public class UserRepository
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
} }
public async Task<dbUser?> GetByIdAsync(int idUser) public async Task<dbUser?> GetByIdAsync(Guid idUser)
{ {
using var db = Connection; using var db = Connection;
return await db.QuerySingleOrDefaultAsync<dbUser>( return await db.QuerySingleOrDefaultAsync<dbUser>(
"sp_User_GetById", "sp_User_GetById",
new { p_id_user = idUser }, new { p_id_user = idUser.ToString() },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
} }
} }
@@ -2,7 +2,7 @@ namespace WinStudentGoalTracker.Models;
public class LoginResponse public class LoginResponse
{ {
public int UserId { get; set; } public Guid UserId { get; set; }
public required string Email { get; set; } public required string Email { get; set; }
public required string Jwt { get; set; } public required string Jwt { get; set; }
public required string RefreshToken { get; set; } public required string RefreshToken { get; set; }
@@ -4,8 +4,8 @@ namespace WinStudentGoalTracker.Models;
public class StudentResponse public class StudentResponse
{ {
public int IdStudent { get; set; } public Guid IdStudent { get; set; }
public int? IdProgram { get; set; } public Guid? IdProgram { get; set; }
public string? Identifier { get; set; } public string? Identifier { get; set; }
public int? ProgramYear { get; set; } public int? ProgramYear { get; set; }
public DateTime? EnrollmentDate { get; set; } public DateTime? EnrollmentDate { get; set; }
+1 -1
View File
@@ -15,7 +15,7 @@ public class TokenService
_config = config; _config = config;
} }
public string GenerateToken(int userId, string email, string? roleName) public string GenerateToken(Guid userId, string email, string? roleName)
{ {
var claims = new List<Claim> var claims = new List<Claim>
{ {