academia/src/backend/Application/Students/Commands/CreateStudentValidator.cs

71 lines
3.2 KiB
C#
Raw Normal View History

namespace Application.Students.Commands;
using System.Text.RegularExpressions;
using Domain.Ports.Repositories;
using FluentValidation;
public partial class CreateStudentValidator : AbstractValidator<CreateStudentCommand>
{
[GeneratedRegex(@"^[\p{L}\s\-'\.]+$", RegexOptions.Compiled)]
private static partial Regex SafeNamePattern();
[GeneratedRegex(@"<[^>]*>|javascript:|on\w+=", RegexOptions.IgnoreCase | RegexOptions.Compiled)]
private static partial Regex DangerousPattern();
public CreateStudentValidator(IStudentRepository studentRepository)
{
RuleFor(x => x.Name)
.NotEmpty().WithMessage("Name is required")
.MinimumLength(3).WithMessage("Name must be at least 3 characters")
.MaximumLength(100).WithMessage("Name must not exceed 100 characters")
.Matches(SafeNamePattern()).WithMessage("Name contains invalid characters")
.Must(NotContainDangerousContent).WithMessage("Name contains prohibited content");
RuleFor(x => x.Email)
.NotEmpty().WithMessage("Email is required")
.MaximumLength(254).WithMessage("Email must not exceed 254 characters")
.EmailAddress().WithMessage("Invalid email format")
.Must(NotContainDangerousContent).WithMessage("Email contains prohibited content")
.MustAsync(async (email, ct) =>
!await studentRepository.EmailExistsAsync(email, null, ct))
.WithMessage("Email already exists");
}
private static bool NotContainDangerousContent(string? value) =>
string.IsNullOrEmpty(value) || !DangerousPattern().IsMatch(value);
}
public partial class UpdateStudentValidator : AbstractValidator<UpdateStudentCommand>
{
[GeneratedRegex(@"^[\p{L}\s\-'\.]+$", RegexOptions.Compiled)]
private static partial Regex SafeNamePattern();
[GeneratedRegex(@"<[^>]*>|javascript:|on\w+=", RegexOptions.IgnoreCase | RegexOptions.Compiled)]
private static partial Regex DangerousPattern();
public UpdateStudentValidator(IStudentRepository studentRepository)
{
RuleFor(x => x.Id)
.GreaterThan(0).WithMessage("Invalid student ID");
RuleFor(x => x.Name)
.NotEmpty().WithMessage("Name is required")
.MinimumLength(3).WithMessage("Name must be at least 3 characters")
.MaximumLength(100).WithMessage("Name must not exceed 100 characters")
.Matches(SafeNamePattern()).WithMessage("Name contains invalid characters")
.Must(NotContainDangerousContent).WithMessage("Name contains prohibited content");
RuleFor(x => x.Email)
.NotEmpty().WithMessage("Email is required")
.MaximumLength(254).WithMessage("Email must not exceed 254 characters")
.EmailAddress().WithMessage("Invalid email format")
.Must(NotContainDangerousContent).WithMessage("Email contains prohibited content")
.MustAsync(async (command, email, ct) =>
!await studentRepository.EmailExistsAsync(email, command.Id, ct))
.WithMessage("Email already exists");
}
private static bool NotContainDangerousContent(string? value) =>
string.IsNullOrEmpty(value) || !DangerousPattern().IsMatch(value);
}