Вытащил солюшен на уровень выше, чтобы прощё было дотнетить
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is failing
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	continuous-integration/drone/push Build is failing
				
			This commit is contained in:
		
							
								
								
									
										15
									
								
								ApplicationLayer/Services/AuthServices/Common/AuthData.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								ApplicationLayer/Services/AuthServices/Common/AuthData.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using Domains; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Common; | ||||
|  | ||||
| public class AuthData | ||||
| { | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.EmailLength)] | ||||
|     public string Email { get; set; } = null!; | ||||
|  | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.PasswordLength)] | ||||
|     public string Password { get; set; } = null!; | ||||
| } | ||||
| @@ -0,0 +1,8 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Common; | ||||
|  | ||||
| public class AuthToken | ||||
| { | ||||
|     [Required] public string Token { get; set; } = null!; | ||||
| } | ||||
| @@ -0,0 +1,29 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
| 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<AuthToken> ILoginService.LoginAsync(LoginRequest request, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (request.AuthData is { Email: "admin@mail.ru", Password: "admin" }) | ||||
|         { | ||||
|             var admin = new User { Role = Role.Admin }; | ||||
|  | ||||
|             return tokenGenerator.CreateToken(admin); | ||||
|         } | ||||
|  | ||||
|         var user = await users.FindByEmailAsync(request.AuthData.Email, cancellationToken); | ||||
|         if (user is null || user.Password != request.AuthData.Password) | ||||
|         { | ||||
|             throw new IncorrectLoginDataException(); | ||||
|         } | ||||
|  | ||||
|         return tokenGenerator.CreateToken(user); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,5 @@ | ||||
| using ApplicationLayer.GeneralExceptions; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.LoginService.Exceptions; | ||||
|  | ||||
| public class IncorrectLoginDataException() : ApiException("Incorrect email or password"); | ||||
| @@ -0,0 +1,12 @@ | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
| using ApplicationLayer.Services.AuthServices.Requests; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.LoginService; | ||||
|  | ||||
| /// Handles login requests | ||||
| public interface ILoginService | ||||
| { | ||||
|     /// Handle login request | ||||
|     /// <returns>JWT-token</returns> | ||||
|     Task<AuthToken> LoginAsync(LoginRequest request, CancellationToken cancellationToken); | ||||
| } | ||||
| @@ -0,0 +1,22 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
| 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<AuthToken> ILoginService.LoginAsync(LoginRequest request, CancellationToken cancellationToken) | ||||
|     { | ||||
|         var user = await users.FindByEmailAsync(request.AuthData.Email, cancellationToken); | ||||
|         if (user is null || user.Password != request.AuthData.Password) | ||||
|         { | ||||
|             throw new IncorrectLoginDataException(); | ||||
|         } | ||||
|  | ||||
|         return tokenGenerator.CreateToken(user); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,20 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using Domains.Users; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.NeededServices; | ||||
|  | ||||
| /// Repository pattern for <see cref="User"/> | ||||
| public interface IUsersRepository : IGenericRepository<User> | ||||
| { | ||||
|     /// Find <see cref="User"/> by email | ||||
|     /// <param name="email"><see cref="User"/>'s email</param> | ||||
|     /// <param name="cancellationToken">Cancellation token</param> | ||||
|     /// <returns>User or null if not found</returns> | ||||
|     Task<User?> FindByEmailAsync(string email, CancellationToken cancellationToken); | ||||
|  | ||||
|     /// Returns all accounts with specific role | ||||
|     /// <param name="role">role</param> | ||||
|     /// <param name="cancellationToken">cancellation token</param> | ||||
|     /// <returns>list of accounts</returns> | ||||
|     Task<List<User>> GetAllOfRoleAsync(Role role, CancellationToken cancellationToken); | ||||
| } | ||||
| @@ -0,0 +1,13 @@ | ||||
| using ApplicationLayer.Services.AuthServices.Requests; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.RegisterService; | ||||
|  | ||||
| /// Handles register request | ||||
| public interface IRegisterService | ||||
| { | ||||
|     /// Handle <see cref="RegisterApplicantRequest"/> | ||||
|     Task RegisterApplicant(RegisterApplicantRequest request, CancellationToken cancellationToken); | ||||
|  | ||||
|     /// Handles <see cref="RegisterRequest"/> and adds approving authority account | ||||
|     Task RegisterAuthority(RegisterRequest request, CancellationToken cancellationToken); | ||||
| } | ||||
| @@ -0,0 +1,41 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using ApplicationLayer.Services.Applicants.NeededServices; | ||||
| using ApplicationLayer.Services.AuthServices.NeededServices; | ||||
| using ApplicationLayer.Services.AuthServices.Requests; | ||||
| using AutoMapper; | ||||
| using Domains.ApplicantDomain; | ||||
| using Domains.Users; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.RegisterService; | ||||
|  | ||||
| /// <inheritdoc cref="IRegisterService"/> | ||||
| public class RegisterService( | ||||
|     IUsersRepository users, | ||||
|     IApplicantsRepository applicants, | ||||
|     IUnitOfWork unitOfWork, | ||||
|     IMapper mapper) : IRegisterService | ||||
| { | ||||
|     async Task IRegisterService.RegisterApplicant(RegisterApplicantRequest request, CancellationToken cancellationToken) | ||||
|     { | ||||
|         var user = mapper.Map<User>(request.RegisterRequest.AuthData); | ||||
|         user.Role = Role.Applicant; | ||||
|  | ||||
|         var applicant = mapper.Map<Applicant>(request); | ||||
|         applicant.UserId = user.Id; | ||||
|  | ||||
|         await users.AddAsync(user, cancellationToken); | ||||
|         await applicants.AddAsync(applicant, cancellationToken); | ||||
|  | ||||
|         await unitOfWork.SaveAsync(cancellationToken); | ||||
|     } | ||||
|  | ||||
|     async Task IRegisterService.RegisterAuthority(RegisterRequest request, CancellationToken cancellationToken) | ||||
|     { | ||||
|         var user = mapper.Map<User>(request.AuthData); | ||||
|         user.Role = Role.ApprovingAuthority; | ||||
|  | ||||
|         await users.AddAsync(user, cancellationToken); | ||||
|  | ||||
|         await unitOfWork.SaveAsync(cancellationToken); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,9 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Requests; | ||||
|  | ||||
| public class LoginRequest | ||||
| { | ||||
|     [Required] public AuthData AuthData { get; set; } = null!; | ||||
| } | ||||
| @@ -0,0 +1,49 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using ApplicationLayer.Services.Applicants.Models; | ||||
| using Domains; | ||||
| using Domains.ApplicantDomain; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Requests; | ||||
|  | ||||
| public record RegisterApplicantRequest | ||||
| { | ||||
|     [Required] public RegisterRequest RegisterRequest { get; set; } = null!; | ||||
|  | ||||
|     [Required] public NameModel ApplicantName { get; set; } = null!; | ||||
|  | ||||
|     [Required] public PassportModel Passport { get; set; } = null!; | ||||
|  | ||||
|     [Required] public DateTime BirthDate { get; set; } | ||||
|  | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.CityNameLength)] | ||||
|     public string CityOfBirth { get; set; } = null!; | ||||
|  | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.CountryNameLength)] | ||||
|     public string CountryOfBirth { get; set; } = null!; | ||||
|  | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.CitizenshipLength)] | ||||
|     public string Citizenship { get; set; } = null!; | ||||
|  | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.CitizenshipLength)] | ||||
|     public string CitizenshipByBirth { get; set; } = null!; | ||||
|  | ||||
|     [Required] public Gender Gender { get; set; } | ||||
|  | ||||
|     [Required] public MaritalStatus MaritalStatus { get; set; } | ||||
|  | ||||
|     [Required] public NameModel FatherName { get; set; } = null!; | ||||
|  | ||||
|     [Required] public NameModel MotherName { get; set; } = null!; | ||||
|  | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.JobTitleLength)] | ||||
|     public string JobTitle { get; set; } = null!; | ||||
|  | ||||
|     [Required] public PlaceOfWorkModel PlaceOfWork { get; set; } = null!; | ||||
|  | ||||
|     [Required] public bool IsNonResident { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,9 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Requests; | ||||
|  | ||||
| public class RegisterRequest | ||||
| { | ||||
|     [Required] public AuthData AuthData { get; set; } = null!; | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
| using Domains; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Requests.Validation; | ||||
|  | ||||
| public class AuthDataValidator : AbstractValidator<AuthData> | ||||
| { | ||||
|     public AuthDataValidator() | ||||
|     { | ||||
|         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}"); | ||||
|  | ||||
|         RuleFor(d => d.Password) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Password can not be empty") | ||||
|             .Matches(Constants.EnglishPhraseRegex) | ||||
|             .WithMessage("Password can contain only english letters, digits and special symbols") | ||||
|             .MaximumLength(ConfigurationConstraints.PasswordLength) | ||||
|             .WithMessage($"Password length must be less than {ConfigurationConstraints.PasswordLength}"); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,93 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using ApplicationLayer.Services.Applicants.Models; | ||||
| using Domains; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Requests.Validation; | ||||
|  | ||||
| public class RegisterApplicantRequestValidator : AbstractValidator<RegisterApplicantRequest> | ||||
| { | ||||
|     public RegisterApplicantRequestValidator( | ||||
|         IDateTimeProvider dateTimeProvider, | ||||
|         IValidator<NameModel> nameValidator, | ||||
|         IValidator<RegisterRequest> registerRequestValidator, | ||||
|         IValidator<PassportModel> passportValidator, | ||||
|         IValidator<PlaceOfWorkModel> placeOfWorkModelValidator) | ||||
|     { | ||||
|         RuleFor(r => r.RegisterRequest) | ||||
|             .NotEmpty() | ||||
|             .SetValidator(registerRequestValidator); | ||||
|  | ||||
|         RuleFor(r => r.ApplicantName) | ||||
|             .NotEmpty() | ||||
|             .SetValidator(nameValidator); | ||||
|  | ||||
|         RuleFor(r => r.FatherName) | ||||
|             .NotEmpty() | ||||
|             .SetValidator(nameValidator); | ||||
|  | ||||
|         RuleFor(r => r.MotherName) | ||||
|             .NotEmpty() | ||||
|             .SetValidator(nameValidator); | ||||
|  | ||||
|         RuleFor(r => r.Passport) | ||||
|             .NotEmpty() | ||||
|             .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") | ||||
|             .Matches(Constants.EnglishPhraseRegex) | ||||
|             .WithMessage("Country of birth field can contain only english letters, digits and special symbols") | ||||
|             .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") | ||||
|             .Matches(Constants.EnglishPhraseRegex) | ||||
|             .WithMessage("City of birth field can contain only english letters, digits and special symbols") | ||||
|             .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") | ||||
|             .Matches(Constants.EnglishPhraseRegex) | ||||
|             .WithMessage("Citizenship field can contain only english letters, digits and special symbols") | ||||
|             .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") | ||||
|             .Matches(Constants.EnglishPhraseRegex) | ||||
|             .WithMessage("Citizenship by birth field can contain only english letters, digits and special symbols") | ||||
|             .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") | ||||
|             .Matches(Constants.EnglishPhraseRegex) | ||||
|             .WithMessage("Title of job field can contain only english letters, digits and special symbols") | ||||
|             .MaximumLength(ConfigurationConstraints.JobTitleLength) | ||||
|             .WithMessage($"Title of job length must be less than {ConfigurationConstraints.JobTitleLength}"); | ||||
|  | ||||
|         RuleFor(r => r.PlaceOfWork) | ||||
|             .NotEmpty() | ||||
|             .SetValidator(placeOfWorkModelValidator); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,17 @@ | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
| using ApplicationLayer.Services.AuthServices.NeededServices; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Requests.Validation; | ||||
|  | ||||
| public class RegisterRequestValidator : AbstractValidator<RegisterRequest> | ||||
| { | ||||
|     public RegisterRequestValidator(IUsersRepository users, IValidator<AuthData> authDataValidator) | ||||
|     { | ||||
|         RuleFor(r => r.AuthData) | ||||
|             .NotEmpty() | ||||
|             .SetValidator(authDataValidator) | ||||
|             .MustAsync(async (authData, ct) => await users.FindByEmailAsync(authData.Email, ct) is null) | ||||
|             .WithMessage("Email already exists"); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user