Added validation and fixed errors

This commit is contained in:
2024-08-25 19:49:28 +03:00
parent c92855e7ce
commit 00aa3ab6af
43 changed files with 621 additions and 147 deletions

View File

@@ -0,0 +1,4 @@
namespace ApplicationLayer.Services.AuthServices.Common
{
public record AuthData(string Email, string Password);
}

View File

@@ -1,23 +1,22 @@
using ApplicationLayer.Services.AuthServices.LoginService.Exceptions;
using ApplicationLayer.Services.AuthServices.NeededServices;
using ApplicationLayer.Services.AuthServices.Requests;
using Domains.Users;
namespace ApplicationLayer.Services.AuthServices.LoginService
{
public class DevelopmentLoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService
{
async Task<string> ILoginService.LoginAsync(UserLoginRequest request, CancellationToken cancellationToken)
async Task<string> ILoginService.LoginAsync(string email, string password, CancellationToken cancellationToken)
{
if (request is { Email: "admin@mail.ru", Password: "admin" })
if (email == "admin@mail.ru" && password == "admin")
{
var admin = new User { Role = Role.Admin };
return tokenGenerator.CreateToken(admin);
}
var user = await users.FindByEmailAsync(request.Email, cancellationToken);
if (user is null || user.Password != request.Password)
var user = await users.FindByEmailAsync(email, cancellationToken);
if (user is null || user.Password != password)
{
throw new IncorrectLoginDataException();
}

View File

@@ -1,12 +1,10 @@
using ApplicationLayer.Services.AuthServices.Requests;
namespace ApplicationLayer.Services.AuthServices.LoginService
namespace ApplicationLayer.Services.AuthServices.LoginService
{
/// Handles <see cref="UserLoginRequest"/>
/// Handles login requests
public interface ILoginService
{
/// Handle <see cref="UserLoginRequest"/>
/// Handle login request
/// <returns>JWT-token</returns>
Task<string> LoginAsync(UserLoginRequest request, CancellationToken cancellationToken);
Task<string> LoginAsync(string email, string password, CancellationToken cancellationToken);
}
}

View File

@@ -1,16 +1,15 @@
using ApplicationLayer.Services.AuthServices.LoginService.Exceptions;
using ApplicationLayer.Services.AuthServices.NeededServices;
using ApplicationLayer.Services.AuthServices.Requests;
namespace ApplicationLayer.Services.AuthServices.LoginService
{
/// <inheritdoc cref="ILoginService"/>
public class LoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService
{
async Task<string> ILoginService.LoginAsync(UserLoginRequest request, CancellationToken cancellationToken)
async Task<string> ILoginService.LoginAsync(string email, string password, CancellationToken cancellationToken)
{
var user = await users.FindByEmailAsync(request.Email, cancellationToken);
if (user is null || user.Password != request.Password)
var user = await users.FindByEmailAsync(email, cancellationToken);
if (user is null || user.Password != password)
{
throw new IncorrectLoginDataException();
}

View File

@@ -3,5 +3,5 @@ using ApplicationLayer.Services.AuthServices.Requests;
namespace ApplicationLayer.Services.AuthServices.RegisterService.Exceptions
{
public class UserAlreadyExistsException(RegisterRequest request) : AlreadyExistsException($"User with email '{request.Email}' already exists");
public class UserAlreadyExistsException(RegisterRequest request) : AlreadyExistsException($"User with email '{request.AuthData.Email}' already exists");
}

View File

@@ -1,7 +1,6 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.Applicants.NeededServices;
using ApplicationLayer.Services.AuthServices.NeededServices;
using ApplicationLayer.Services.AuthServices.RegisterService.Exceptions;
using ApplicationLayer.Services.AuthServices.Requests;
using AutoMapper;
using Domains.ApplicantDomain;
@@ -18,33 +17,11 @@ namespace ApplicationLayer.Services.AuthServices.RegisterService
{
async Task IRegisterService.RegisterApplicant(RegisterApplicantRequest request, CancellationToken cancellationToken)
{
//todo move to validation layer
if (await users.FindByEmailAsync(request.Email, cancellationToken) is not null)
{
throw new UserAlreadyExistsException(request);
}
var user = mapper.Map<User>(request);
var user = mapper.Map<User>(request.AuthData);
user.Role = Role.Applicant;
var applicant = new Applicant
{
Citizenship = request.Citizenship,
CitizenshipByBirth = request.CitizenshipByBirth,
Gender = request.Gender,
Name = request.ApplicantName,
Passport = request.Passport,
BirthDate = request.BirthDate,
FatherName = request.FatherName,
JobTitle = request.JobTitle,
MaritalStatus = request.MaritalStatus,
MotherName = request.MotherName,
UserId = user.Id,
CityOfBirth = request.CityOfBirth,
CountryOfBirth = request.CountryOfBirth,
IsNonResident = request.IsNonResident,
PlaceOfWork = request.PlaceOfWork
};
var applicant = mapper.Map<Applicant>(request);
applicant.UserId = user.Id;
await users.AddAsync(user, cancellationToken);
await applicants.AddAsync(applicant, cancellationToken);
@@ -54,13 +31,7 @@ namespace ApplicationLayer.Services.AuthServices.RegisterService
async Task IRegisterService.RegisterAuthority(RegisterRequest request, CancellationToken cancellationToken)
{
//todo move to validation layer
if (await users.FindByEmailAsync(request.Email, cancellationToken) is not null)
{
throw new UserAlreadyExistsException(request);
}
var user = mapper.Map<User>(request);
var user = mapper.Map<User>(request.AuthData);
user.Role = Role.ApprovingAuthority;
await users.AddAsync(user, cancellationToken);

View File

@@ -1,10 +1,11 @@
using Domains.ApplicantDomain;
using ApplicationLayer.Services.Applicants.Models;
using ApplicationLayer.Services.AuthServices.Common;
using Domains.ApplicantDomain;
namespace ApplicationLayer.Services.AuthServices.Requests
{
public record RegisterApplicantRequest(
string Email,
string Password,
AuthData AuthData,
Name ApplicantName,
Passport Passport,
DateTime BirthDate,
@@ -17,6 +18,6 @@ namespace ApplicationLayer.Services.AuthServices.Requests
Name FatherName,
Name MotherName,
string JobTitle,
PlaceOfWork PlaceOfWork,
bool IsNonResident) : RegisterRequest(Email, Password);
PlaceOfWorkModel PlaceOfWork,
bool IsNonResident) : RegisterRequest(AuthData);
}

View File

@@ -1,4 +1,6 @@
namespace ApplicationLayer.Services.AuthServices.Requests
using ApplicationLayer.Services.AuthServices.Common;
namespace ApplicationLayer.Services.AuthServices.Requests
{
public record RegisterRequest(string Email, string Password);
public record RegisterRequest(AuthData AuthData);
}

View File

@@ -1,4 +0,0 @@
namespace ApplicationLayer.Services.AuthServices.Requests
{
public record UserLoginRequest(string Email, string Password);
}

View File

@@ -0,0 +1,32 @@
using ApplicationLayer.Services.AuthServices.Common;
using ApplicationLayer.Services.AuthServices.NeededServices;
using Domains;
using FluentValidation;
namespace ApplicationLayer.Services.AuthServices.Requests.Validation
{
public class AuthDataValidator : AbstractValidator<AuthData>
{
public AuthDataValidator(IUsersRepository users)
{
RuleFor(d => d.Email)
.NotEmpty()
.WithMessage("Email can not be empty")
.EmailAddress()
.WithMessage("Email must be valid")
.MaximumLength(ConfigurationConstraints.EmailLength)
.WithMessage($"Email length must be less than {ConfigurationConstraints.EmailLength}")
.MustAsync(async (email, ct) =>
{
return await users.FindByEmailAsync(email, ct) is null;
})
.WithMessage("Email already exists");
RuleFor(d => d.Password)
.NotEmpty()
.WithMessage("Password can not be empty")
.MaximumLength(ConfigurationConstraints.PasswordLength)
.WithMessage($"Password length must be less than {ConfigurationConstraints.PasswordLength}");
}
}
}

View File

@@ -0,0 +1,28 @@
using Domains;
using Domains.ApplicantDomain;
using FluentValidation;
namespace ApplicationLayer.Services.AuthServices.Requests.Validation
{
public class NameValidator : AbstractValidator<Name>
{
public NameValidator()
{
RuleFor(m => m.FirstName)
.NotEmpty()
.WithMessage("First Name can not be empty")
.MaximumLength(ConfigurationConstraints.NameLength)
.WithMessage($"First Name length must be less than {ConfigurationConstraints.NameLength}");
RuleFor(m => m.Surname)
.NotEmpty()
.WithMessage("Surname can not be empty")
.MaximumLength(ConfigurationConstraints.NameLength)
.WithMessage($"Surname length must be less than {ConfigurationConstraints.NameLength}");
RuleFor(m => m.Patronymic)
.MaximumLength(ConfigurationConstraints.NameLength)
.WithMessage($"Patronymic length must be less than {ConfigurationConstraints.NameLength}");
}
}
}

View File

@@ -0,0 +1,37 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using Domains;
using Domains.ApplicantDomain;
using FluentValidation;
namespace ApplicationLayer.Services.AuthServices.Requests.Validation
{
public class PassportValidator : AbstractValidator<Passport>
{
public PassportValidator(IDateTimeProvider dateTimeProvider)
{
RuleFor(r => r.Issuer)
.NotEmpty()
.WithMessage("Passport issuer can not be empty")
.MaximumLength(ConfigurationConstraints.IssuerNameLength)
.WithMessage($"Passport issuer length must be less than {ConfigurationConstraints.IssuerNameLength}");
RuleFor(r => r.Number)
.NotEmpty()
.WithMessage("Passport number can not be empty")
.MaximumLength(ConfigurationConstraints.PassportNumberLength)
.WithMessage($"Passport number length must be less than {ConfigurationConstraints.PassportNumberLength}");
RuleFor(r => r.ExpirationDate)
.NotEmpty()
.WithMessage("Passport expiration date can not be empty")
.GreaterThan(dateTimeProvider.Now())
.WithMessage("Can not approve visa for applicants with expired passport");
RuleFor(r => r.IssueDate)
.NotEmpty()
.WithMessage("Passport issue date can not be empty")
.LessThanOrEqualTo(dateTimeProvider.Now())
.WithMessage("Passport issue date must be in past");
}
}
}

View File

@@ -0,0 +1,50 @@
using ApplicationLayer.Services.Applicants.Models;
using Domains;
using FluentValidation;
namespace ApplicationLayer.Services.AuthServices.Requests.Validation
{
public class PlaceOfWorkModelValidator : AbstractValidator<PlaceOfWorkModel>
{
public PlaceOfWorkModelValidator()
{
RuleFor(p => p.Name)
.NotEmpty()
.WithMessage("Place of work name can not be empty")
.MaximumLength(ConfigurationConstraints.PlaceOfWorkNameLength)
.WithMessage($"Place of work name length must be less than {ConfigurationConstraints.PlaceOfWorkNameLength}");
RuleFor(p => p.PhoneNum)
.NotEmpty()
.WithMessage("Place of work phone number can not be empty")
.MaximumLength(ConfigurationConstraints.PhoneNumberLength)
.WithMessage($"Phone number length must be in range from {ConfigurationConstraints.PhoneNumberMinLength} to {ConfigurationConstraints.PhoneNumberLength}")
.MinimumLength(ConfigurationConstraints.PhoneNumberMinLength)
.WithMessage($"Phone number length must be in range from {ConfigurationConstraints.PhoneNumberMinLength} to {ConfigurationConstraints.PhoneNumberLength}");
RuleFor(p => p.Address.Country)
.NotEmpty()
.WithMessage("Country name of place of work can not be empty")
.MaximumLength(ConfigurationConstraints.CountryNameLength)
.WithMessage($"Country name of place of work length must be less than {ConfigurationConstraints.CountryNameLength}");
RuleFor(p => p.Address.City)
.NotEmpty()
.WithMessage("City name of place of work can not be empty")
.MaximumLength(ConfigurationConstraints.CityNameLength)
.WithMessage($"City name of place of work length must be less than {ConfigurationConstraints.CityNameLength}");
RuleFor(p => p.Address.Street)
.NotEmpty()
.WithMessage("Street name of place of work can not be empty")
.MaximumLength(ConfigurationConstraints.StreetNameLength)
.WithMessage($"Street name of place of work length must be less than {ConfigurationConstraints.StreetNameLength}");
RuleFor(p => p.Address.Building)
.NotEmpty()
.WithMessage("Building of place of work can not be empty")
.MaximumLength(ConfigurationConstraints.CountryNameLength)
.WithMessage($"Building of place of work length must be less than {ConfigurationConstraints.BuildingNumberLength}");
}
}
}

View File

@@ -0,0 +1,78 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.Applicants.Models;
using ApplicationLayer.Services.AuthServices.Common;
using Domains;
using Domains.ApplicantDomain;
using FluentValidation;
namespace ApplicationLayer.Services.AuthServices.Requests.Validation
{
public class RegisterApplicantRequestValidator : AbstractValidator<RegisterApplicantRequest>
{
public RegisterApplicantRequestValidator(
IDateTimeProvider dateTimeProvider,
IValidator<Name> nameValidator,
IValidator<AuthData> authDataValidator,
IValidator<Passport> passportValidator,
IValidator<PlaceOfWorkModel> placeOfWorkModelValidator)
{
RuleFor(r => r.AuthData)
.SetValidator(authDataValidator);
RuleFor(r => r.ApplicantName)
.SetValidator(nameValidator);
RuleFor(r => r.FatherName)
.SetValidator(nameValidator);
RuleFor(r => r.MotherName)
.SetValidator(nameValidator);
RuleFor(r => r.Passport)
.SetValidator(passportValidator);
RuleFor(r => r.BirthDate)
.NotEmpty()
.WithMessage("Birth date can not be empty")
.LessThanOrEqualTo(dateTimeProvider.Now().AddYears(-ConfigurationConstraints.ApplicantMinAge))
.WithMessage($"Applicant must be older than {ConfigurationConstraints.ApplicantMinAge}");
RuleFor(r => r.CountryOfBirth)
.NotEmpty()
.WithMessage("Country of birth can not be empty")
.MaximumLength(ConfigurationConstraints.CountryNameLength)
.WithMessage($"Country of birth name length must be less than {ConfigurationConstraints.CountryNameLength}");
RuleFor(r => r.CityOfBirth)
.NotEmpty()
.WithMessage("City of birth can not be empty")
.MaximumLength(ConfigurationConstraints.CityNameLength)
.WithMessage($"City of birth name length must be less than {ConfigurationConstraints.CityNameLength}");
RuleFor(r => r.Citizenship)
.NotEmpty()
.WithMessage("Citizenship can not be empty")
.MaximumLength(ConfigurationConstraints.CitizenshipLength)
.WithMessage($"Citizenship length must be less than {ConfigurationConstraints.CitizenshipLength}");
RuleFor(r => r.CitizenshipByBirth)
.NotEmpty()
.WithMessage("Citizenship by birth can not be empty")
.MaximumLength(ConfigurationConstraints.CitizenshipLength)
.WithMessage($"Citizenship by birth length must be less than {ConfigurationConstraints.CitizenshipLength}");
RuleFor(r => r.Gender).IsInEnum();
RuleFor(r => r.MaritalStatus).IsInEnum();
RuleFor(r => r.JobTitle)
.NotEmpty()
.WithMessage("Title of job can not be empty")
.MaximumLength(ConfigurationConstraints.JobTitleLength)
.WithMessage($"Title of job length must be less than {ConfigurationConstraints.JobTitleLength}");
RuleFor(r => r.PlaceOfWork)
.SetValidator(placeOfWorkModelValidator);
}
}
}