71 lines
3.2 KiB
C#
71 lines
3.2 KiB
C#
|
|
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);
|
||
|
|
}
|