Added authentication and authorization, updated dependency injections, removed hard-coded connection string
This commit is contained in:
		| @@ -14,4 +14,8 @@ | |||||||
|       <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0-preview.7.24405.7" /> |       <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0-preview.7.24405.7" /> | ||||||
|     </ItemGroup> |     </ItemGroup> | ||||||
|  |  | ||||||
|  |     <ItemGroup> | ||||||
|  |       <Folder Include="DataAccessingServices\" /> | ||||||
|  |     </ItemGroup> | ||||||
|  |  | ||||||
| </Project> | </Project> | ||||||
|   | |||||||
| @@ -0,0 +1,4 @@ | |||||||
|  | namespace ApplicationLayer.AuthServices.LoginService.Exceptions | ||||||
|  | { | ||||||
|  |     public class IncorrectLoginDataException() : Exception("Incorrect email or password"); | ||||||
|  | } | ||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | using ApplicationLayer.AuthServices.Requests; | ||||||
|  |  | ||||||
|  | namespace ApplicationLayer.AuthServices.LoginService | ||||||
|  | { | ||||||
|  |     /// Handles <see cref="UserLoginRequest"/> | ||||||
|  |     public interface ILoginService | ||||||
|  |     { | ||||||
|  |         /// Handle <see cref="UserLoginRequest"/> | ||||||
|  |         /// <returns>JWT-token</returns> | ||||||
|  |         Task<string> LoginAsync(UserLoginRequest request, CancellationToken cancellationToken); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,21 @@ | |||||||
|  | using ApplicationLayer.AuthServices.LoginService.Exceptions; | ||||||
|  | using ApplicationLayer.AuthServices.NeededServices; | ||||||
|  | using ApplicationLayer.AuthServices.Requests; | ||||||
|  |  | ||||||
|  | namespace ApplicationLayer.AuthServices.LoginService | ||||||
|  | { | ||||||
|  |     /// <inheritdoc cref="ILoginService"/> | ||||||
|  |     public class LoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService | ||||||
|  |     { | ||||||
|  |         async Task<string> ILoginService.LoginAsync(UserLoginRequest request, CancellationToken cancellationToken) | ||||||
|  |         { | ||||||
|  |             var user = await users.FindByEmailAsync(request.Email, cancellationToken); | ||||||
|  |             if (user is null || user.Password != request.Password) | ||||||
|  |             { | ||||||
|  |                 throw new IncorrectLoginDataException(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return tokenGenerator.CreateToken(user); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | using Domains.Users; | ||||||
|  |  | ||||||
|  | namespace ApplicationLayer.AuthServices.NeededServices | ||||||
|  | { | ||||||
|  |     public interface ITokenGenerator | ||||||
|  |     { | ||||||
|  |         string CreateToken(User user); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,15 @@ | |||||||
|  | using ApplicationLayer.GeneralNeededServices; | ||||||
|  | using Domains.Users; | ||||||
|  |  | ||||||
|  | namespace ApplicationLayer.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); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,6 @@ | |||||||
|  | using ApplicationLayer.AuthServices.Requests; | ||||||
|  |  | ||||||
|  | namespace ApplicationLayer.AuthServices.RegisterService.Exceptions | ||||||
|  | { | ||||||
|  |     public class UserAlreadyExistsException(RegisterApplicantRequest request) : Exception($"User with email '{request.Email}' already exists"); | ||||||
|  | } | ||||||
| @@ -0,0 +1,11 @@ | |||||||
|  | using ApplicationLayer.AuthServices.Requests; | ||||||
|  |  | ||||||
|  | namespace ApplicationLayer.AuthServices.RegisterService | ||||||
|  | { | ||||||
|  |     /// Handles <see cref="RegisterApplicantRequest"/> | ||||||
|  |     public interface IRegisterService | ||||||
|  |     { | ||||||
|  |         /// Handle <see cref="RegisterApplicantRequest"/> | ||||||
|  |         Task Register(RegisterApplicantRequest request, CancellationToken cancellationToken); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,31 @@ | |||||||
|  | using ApplicationLayer.AuthServices.NeededServices; | ||||||
|  | using ApplicationLayer.AuthServices.RegisterService.Exceptions; | ||||||
|  | using ApplicationLayer.AuthServices.Requests; | ||||||
|  | using Domains.Users; | ||||||
|  |  | ||||||
|  | namespace ApplicationLayer.AuthServices.RegisterService | ||||||
|  | { | ||||||
|  |     /// <inheritdoc cref="IRegisterService"/> | ||||||
|  |     public class RegisterService(IUsersRepository users) : IRegisterService | ||||||
|  |     { | ||||||
|  |         async Task IRegisterService.Register(RegisterApplicantRequest request, CancellationToken cancellationToken) | ||||||
|  |         { | ||||||
|  |             if (await users.FindByEmailAsync(request.Email, cancellationToken) is not null) | ||||||
|  |             { | ||||||
|  |                 throw new UserAlreadyExistsException(request); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             //TODO mapper | ||||||
|  |             var user = new User | ||||||
|  |             { | ||||||
|  |                 Email = request.Email, | ||||||
|  |                 Password = request.Password, | ||||||
|  |                 Role = Role.Applicant | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             await users.AddAsync(user, cancellationToken); | ||||||
|  |             await users.SaveAsync(cancellationToken); | ||||||
|  |             users.GetAllAsync(cancellationToken); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,4 @@ | |||||||
|  | namespace ApplicationLayer.AuthServices.Requests | ||||||
|  | { | ||||||
|  |     public record RegisterApplicantRequest(string Email, string Password); | ||||||
|  | } | ||||||
| @@ -0,0 +1,4 @@ | |||||||
|  | namespace ApplicationLayer.AuthServices.Requests | ||||||
|  | { | ||||||
|  |     public record UserLoginRequest(string Email, string Password); | ||||||
|  | } | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| using ApplicationLayer.GeneralNeededServices; | using ApplicationLayer.GeneralNeededServices; | ||||||
| using Domains.ApplicantDomain; | using Domains.ApplicantDomain; | ||||||
| 
 | 
 | ||||||
| namespace ApplicationLayer.Applicants.NeededServices; | namespace ApplicationLayer.DataAccessingServices.Applicants.NeededServices; | ||||||
| 
 | 
 | ||||||
| /// Repository pattern for <see cref="Applicant"/> | /// Repository pattern for <see cref="Applicant"/> | ||||||
| public interface IApplicantsRepository : IGenericRepository<Applicant> { } | public interface IApplicantsRepository : IGenericRepository<Applicant>; | ||||||
| @@ -0,0 +1,6 @@ | |||||||
|  | using ApplicationLayer.GeneralNeededServices; | ||||||
|  | using Domains.LocationDomain; | ||||||
|  |  | ||||||
|  | namespace ApplicationLayer.DataAccessingServices.Locations.NeededServices; | ||||||
|  |  | ||||||
|  | public interface ICitiesRepository : IGenericRepository<City>; | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| using ApplicationLayer.GeneralNeededServices; | using ApplicationLayer.GeneralNeededServices; | ||||||
| using Domains.LocationDomain; | using Domains.LocationDomain; | ||||||
| 
 | 
 | ||||||
| namespace ApplicationLayer.Locations.NeededServices; | namespace ApplicationLayer.DataAccessingServices.Locations.NeededServices; | ||||||
| 
 | 
 | ||||||
| public interface ICountriesRepository : IGenericRepository<Country> { } | public interface ICountriesRepository : IGenericRepository<Country>; | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| using ApplicationLayer.VisaApplications.Requests; | using ApplicationLayer.DataAccessingServices.VisaApplications.Requests; | ||||||
| using Domains.VisaApplicationDomain; | using Domains.VisaApplicationDomain; | ||||||
| 
 | 
 | ||||||
| namespace ApplicationLayer.VisaApplications.Handlers; | namespace ApplicationLayer.DataAccessingServices.VisaApplications.Handlers; | ||||||
| 
 | 
 | ||||||
| public interface IVisaApplicationsRequestHandler | public interface IVisaApplicationsRequestHandler | ||||||
| { | { | ||||||
| @@ -1,11 +1,11 @@ | |||||||
| using ApplicationLayer.Locations.NeededServices; | using ApplicationLayer.DataAccessingServices.Locations.NeededServices; | ||||||
| using ApplicationLayer.VisaApplications.Models; | using ApplicationLayer.DataAccessingServices.VisaApplications.Models; | ||||||
| using ApplicationLayer.VisaApplications.NeededServices; | using ApplicationLayer.DataAccessingServices.VisaApplications.NeededServices; | ||||||
| using ApplicationLayer.VisaApplications.Requests; | using ApplicationLayer.DataAccessingServices.VisaApplications.Requests; | ||||||
| using Domains.ApplicantDomain; | using Domains.ApplicantDomain; | ||||||
| using Domains.VisaApplicationDomain; | using Domains.VisaApplicationDomain; | ||||||
| 
 | 
 | ||||||
| namespace ApplicationLayer.VisaApplications.Handlers; | namespace ApplicationLayer.DataAccessingServices.VisaApplications.Handlers; | ||||||
| 
 | 
 | ||||||
| /// Handles visa requests | /// Handles visa requests | ||||||
| public class VisaApplicationRequestsHandler( | public class VisaApplicationRequestsHandler( | ||||||
| @@ -64,6 +64,7 @@ public class VisaApplicationRequestsHandler( | |||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         await applications.AddAsync(visaApplication, cancellationToken); |         await applications.AddAsync(visaApplication, cancellationToken); | ||||||
|  |         await applications.SaveAsync(cancellationToken); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private async Task<PastVisit> ConvertPastVisitModelToPastVisit(PastVisitModel model, CancellationToken cancellationToken) |     private async Task<PastVisit> ConvertPastVisitModelToPastVisit(PastVisitModel model, CancellationToken cancellationToken) | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| namespace ApplicationLayer.VisaApplications.Models; | namespace ApplicationLayer.DataAccessingServices.VisaApplications.Models; | ||||||
| 
 | 
 | ||||||
| public class AddressModel | public class AddressModel | ||||||
| { | { | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| namespace ApplicationLayer.VisaApplications.Models | namespace ApplicationLayer.DataAccessingServices.VisaApplications.Models | ||||||
| { | { | ||||||
|     public class PastVisitModel |     public class PastVisitModel | ||||||
|     { |     { | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| namespace ApplicationLayer.VisaApplications.Models; | namespace ApplicationLayer.DataAccessingServices.VisaApplications.Models; | ||||||
| 
 | 
 | ||||||
| public class PlaceOfWorkModel | public class PlaceOfWorkModel | ||||||
| { | { | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| using ApplicationLayer.GeneralNeededServices; | using ApplicationLayer.GeneralNeededServices; | ||||||
| using Domains.VisaApplicationDomain; | using Domains.VisaApplicationDomain; | ||||||
| 
 | 
 | ||||||
| namespace ApplicationLayer.VisaApplications.NeededServices; | namespace ApplicationLayer.DataAccessingServices.VisaApplications.NeededServices; | ||||||
| 
 | 
 | ||||||
| public interface IVisaApplicationsRepository : IGenericRepository<VisaApplication> { } | public interface IVisaApplicationsRepository : IGenericRepository<VisaApplication>; | ||||||
| @@ -1,8 +1,8 @@ | |||||||
| using ApplicationLayer.VisaApplications.Models; | using ApplicationLayer.DataAccessingServices.VisaApplications.Models; | ||||||
| using Domains.ApplicantDomain; | using Domains.ApplicantDomain; | ||||||
| using Domains.VisaApplicationDomain; | using Domains.VisaApplicationDomain; | ||||||
| 
 | 
 | ||||||
| namespace ApplicationLayer.VisaApplications.Requests; | namespace ApplicationLayer.DataAccessingServices.VisaApplications.Requests; | ||||||
| 
 | 
 | ||||||
| /// Model of visa request from user | /// Model of visa request from user | ||||||
| public record VisaApplicationCreateRequest( | public record VisaApplicationCreateRequest( | ||||||
| @@ -1,4 +1,6 @@ | |||||||
| using ApplicationLayer.VisaApplications.Handlers; | using ApplicationLayer.AuthServices.LoginService; | ||||||
|  | using ApplicationLayer.AuthServices.RegisterService; | ||||||
|  | using ApplicationLayer.DataAccessingServices.VisaApplications.Handlers; | ||||||
| using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||||
|  |  | ||||||
| namespace ApplicationLayer; | namespace ApplicationLayer; | ||||||
| @@ -11,6 +13,9 @@ public static class DependencyInjection | |||||||
|     { |     { | ||||||
|         services.AddScoped<IVisaApplicationsRequestHandler, VisaApplicationRequestsHandler>(); |         services.AddScoped<IVisaApplicationsRequestHandler, VisaApplicationRequestsHandler>(); | ||||||
|  |  | ||||||
|  |         services.AddScoped<IRegisterService, RegisterService>(); | ||||||
|  |         services.AddScoped<ILoginService, LoginService>(); | ||||||
|  |  | ||||||
|         return services; |         return services; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | namespace ApplicationLayer.GeneralNeededServices | ||||||
|  | { | ||||||
|  |     public interface IDateTimeProvider | ||||||
|  |     { | ||||||
|  |         /// Returns current date and time | ||||||
|  |         DateTime Now(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,6 +0,0 @@ | |||||||
| using ApplicationLayer.GeneralNeededServices; |  | ||||||
| using Domains.LocationDomain; |  | ||||||
|  |  | ||||||
| namespace ApplicationLayer.Locations.NeededServices; |  | ||||||
|  |  | ||||||
| public interface ICitiesRepository : IGenericRepository<City> { } |  | ||||||
							
								
								
									
										13
									
								
								SchengenVisaApi/Domains/Users/Role.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								SchengenVisaApi/Domains/Users/Role.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | namespace Domains.Users | ||||||
|  | { | ||||||
|  |     /// Role of <see cref="User"/> | ||||||
|  |     public enum Role | ||||||
|  |     { | ||||||
|  |         /// Requests visa applications | ||||||
|  |         Applicant, | ||||||
|  |         /// Approves or declines applications | ||||||
|  |         ApprovingAuthority, | ||||||
|  |         /// Manages approving authorities | ||||||
|  |         Admin | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								SchengenVisaApi/Domains/Users/User.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								SchengenVisaApi/Domains/Users/User.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | namespace Domains.Users | ||||||
|  | { | ||||||
|  |     public class User : IEntity | ||||||
|  |     { | ||||||
|  |         /// Unique Identifier of <see cref="User"/> | ||||||
|  |         public Guid Id { get; private set; } = Guid.NewGuid(); | ||||||
|  |  | ||||||
|  |         public Role Role { get; set; } | ||||||
|  |  | ||||||
|  |         public string Email { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         public string Password { get; set; } = null!; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,24 @@ | |||||||
|  | using System.IdentityModel.Tokens.Jwt; | ||||||
|  | using ApplicationLayer.AuthServices.NeededServices; | ||||||
|  | using ApplicationLayer.GeneralNeededServices; | ||||||
|  | using Microsoft.Extensions.DependencyInjection; | ||||||
|  |  | ||||||
|  | namespace Infrastructure.Auth | ||||||
|  | { | ||||||
|  |     public static class ServiceCollectionsExtensions | ||||||
|  |     { | ||||||
|  |         public static IServiceCollection AddTokenGenerator(this IServiceCollection services, TokenGeneratorOptions options) | ||||||
|  |         { | ||||||
|  |             services.AddSingleton<JwtSecurityTokenHandler>(); | ||||||
|  |             services.AddSingleton<ITokenGenerator, TokenGenerator>(provider => | ||||||
|  |             { | ||||||
|  |                 var tokenHandler = provider.GetRequiredService<JwtSecurityTokenHandler>(); | ||||||
|  |                 var dateTimeProvider = provider.GetRequiredService<IDateTimeProvider>(); | ||||||
|  |  | ||||||
|  |                 return new TokenGenerator(options, tokenHandler, dateTimeProvider); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             return services; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								SchengenVisaApi/Infrastructure/Auth/TokenGenerator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								SchengenVisaApi/Infrastructure/Auth/TokenGenerator.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | using System.IdentityModel.Tokens.Jwt; | ||||||
|  | using System.Security.Claims; | ||||||
|  | using ApplicationLayer.AuthServices.NeededServices; | ||||||
|  | using ApplicationLayer.GeneralNeededServices; | ||||||
|  | using Domains.Users; | ||||||
|  |  | ||||||
|  | namespace Infrastructure.Auth | ||||||
|  | { | ||||||
|  |     public class TokenGenerator(TokenGeneratorOptions options, JwtSecurityTokenHandler tokenHandler, IDateTimeProvider dateTimeProvider) | ||||||
|  |         : ITokenGenerator | ||||||
|  |     { | ||||||
|  |         public string CreateToken(User user) | ||||||
|  |         { | ||||||
|  |             var claims = new List<Claim> | ||||||
|  |             { | ||||||
|  |                 new(ClaimTypes.Role, user.Role.ToString()), | ||||||
|  |                 new(ClaimTypes.Email, user.Email) | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             var token = new JwtSecurityToken( | ||||||
|  |                 issuer: options.Issuer, | ||||||
|  |                 audience: options.Audience, | ||||||
|  |                 expires: dateTimeProvider.Now().Add(options.ValidTime), | ||||||
|  |                 signingCredentials: options.Credentials, | ||||||
|  |                 claims: claims); | ||||||
|  |  | ||||||
|  |             return tokenHandler.WriteToken(token); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,6 @@ | |||||||
|  | using Microsoft.IdentityModel.Tokens; | ||||||
|  |  | ||||||
|  | namespace Infrastructure.Auth | ||||||
|  | { | ||||||
|  |     public record TokenGeneratorOptions(string Issuer, string Audience, TimeSpan ValidTime, SigningCredentials Credentials); | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								SchengenVisaApi/Infrastructure/Common/DateTimeProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								SchengenVisaApi/Infrastructure/Common/DateTimeProvider.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | using ApplicationLayer.GeneralNeededServices; | ||||||
|  |  | ||||||
|  | namespace Infrastructure.Common | ||||||
|  | { | ||||||
|  |     /// Implements <see cref="IDateTimeProvider"/> | ||||||
|  |     public class DateTimeProvider : IDateTimeProvider | ||||||
|  |     { | ||||||
|  |         DateTime IDateTimeProvider.Now() => DateTime.Now; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| using ApplicationLayer.Applicants.NeededServices; | using ApplicationLayer.DataAccessingServices.Applicants.NeededServices; | ||||||
| using Domains.ApplicantDomain; | using Domains.ApplicantDomain; | ||||||
| using Infrastructure.Database.Generic; | using Infrastructure.Database.Generic; | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| using ApplicationLayer.Locations.NeededServices; | using ApplicationLayer.DataAccessingServices.Locations.NeededServices; | ||||||
| using Domains.LocationDomain; | using Domains.LocationDomain; | ||||||
| using Infrastructure.Database.Generic; | using Infrastructure.Database.Generic; | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| using ApplicationLayer.Locations.NeededServices; | using ApplicationLayer.DataAccessingServices.Locations.NeededServices; | ||||||
| using Domains.LocationDomain; | using Domains.LocationDomain; | ||||||
| using Infrastructure.Database.Generic; | using Infrastructure.Database.Generic; | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
|   | |||||||
| @@ -0,0 +1,22 @@ | |||||||
|  | using Domains.Users; | ||||||
|  | using Microsoft.EntityFrameworkCore; | ||||||
|  | using Microsoft.EntityFrameworkCore.Metadata.Builders; | ||||||
|  |  | ||||||
|  | namespace Infrastructure.Database.Users.Configuration | ||||||
|  | { | ||||||
|  |     public class UserConfiguration : IEntityTypeConfiguration<User> | ||||||
|  |     { | ||||||
|  |         public void Configure(EntityTypeBuilder<User> entity) | ||||||
|  |         { | ||||||
|  |             entity.Property(u => u.Email) | ||||||
|  |                 .IsUnicode(false) | ||||||
|  |                 .HasMaxLength(254); | ||||||
|  |  | ||||||
|  |             entity.HasIndex(u => u.Email).IsUnique(); | ||||||
|  |  | ||||||
|  |             entity.Property(u => u.Password) | ||||||
|  |                 .IsUnicode(false) | ||||||
|  |                 .HasMaxLength(50); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,17 @@ | |||||||
|  | using ApplicationLayer.AuthServices.NeededServices; | ||||||
|  | using Domains.Users; | ||||||
|  | using Infrastructure.Database.Generic; | ||||||
|  | using Microsoft.EntityFrameworkCore; | ||||||
|  |  | ||||||
|  | namespace Infrastructure.Database.Users.Repositories | ||||||
|  | { | ||||||
|  |     /// <inheritdoc cref="IUsersRepository"/> | ||||||
|  |     public class UsersRepository(IGenericReader reader, IGenericWriter writer, IUnitOfWork unitOfWork) | ||||||
|  |         : GenericRepository<User>(reader, writer, unitOfWork), IUsersRepository | ||||||
|  |     { | ||||||
|  |         async Task<User?> IUsersRepository.FindByEmailAsync(string email, CancellationToken cancellationToken) | ||||||
|  |         { | ||||||
|  |             return await LoadDomain().SingleOrDefaultAsync(u => u.Email == email, cancellationToken); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| using ApplicationLayer.VisaApplications.NeededServices; | using ApplicationLayer.DataAccessingServices.VisaApplications.NeededServices; | ||||||
| using Domains.VisaApplicationDomain; | using Domains.VisaApplicationDomain; | ||||||
| using Infrastructure.Database.Generic; | using Infrastructure.Database.Generic; | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
|   | |||||||
| @@ -1,13 +1,19 @@ | |||||||
| using ApplicationLayer.Applicants.NeededServices; | using ApplicationLayer.AuthServices.NeededServices; | ||||||
| using ApplicationLayer.Locations.NeededServices; | using ApplicationLayer.DataAccessingServices.Applicants.NeededServices; | ||||||
| using ApplicationLayer.VisaApplications.NeededServices; | using ApplicationLayer.DataAccessingServices.Locations.NeededServices; | ||||||
|  | using ApplicationLayer.DataAccessingServices.VisaApplications.NeededServices; | ||||||
|  | using ApplicationLayer.GeneralNeededServices; | ||||||
|  | using Infrastructure.Auth; | ||||||
|  | using Infrastructure.Common; | ||||||
| using Infrastructure.Database; | using Infrastructure.Database; | ||||||
| using Infrastructure.Database.Applicants.Repositories; | using Infrastructure.Database.Applicants.Repositories; | ||||||
| using Infrastructure.Database.Generic; | using Infrastructure.Database.Generic; | ||||||
| using Infrastructure.Database.Locations.Repositories.Cities; | using Infrastructure.Database.Locations.Repositories.Cities; | ||||||
| using Infrastructure.Database.Locations.Repositories.Countries; | using Infrastructure.Database.Locations.Repositories.Countries; | ||||||
|  | using Infrastructure.Database.Users.Repositories; | ||||||
| using Infrastructure.Database.VisaApplications.Repositories; | using Infrastructure.Database.VisaApplications.Repositories; | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
|  | using Microsoft.Extensions.Configuration; | ||||||
| using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||||
| using DbContext = Infrastructure.Database.DbContext; | using DbContext = Infrastructure.Database.DbContext; | ||||||
|  |  | ||||||
| @@ -17,11 +23,14 @@ namespace Infrastructure; | |||||||
| public static class DependencyInjection | public static class DependencyInjection | ||||||
| { | { | ||||||
|     /// Add services needed for Infrastructure layer |     /// Add services needed for Infrastructure layer | ||||||
|     public static IServiceCollection AddInfrastructure(this IServiceCollection services) |     public static IServiceCollection AddInfrastructure(this IServiceCollection services, | ||||||
|  |         IConfigurationManager configurationManager, | ||||||
|  |         bool isDevelopment) | ||||||
|     { |     { | ||||||
|         //TODO строка подключения |         var databaseName = isDevelopment ? "developmentDB" : "normal'naya database"; | ||||||
|  |  | ||||||
|         services.AddDbContextFactory<DbContext>(opts => |         services.AddDbContextFactory<DbContext>(opts => | ||||||
|             opts.UseSqlServer("Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=visadb;Integrated Security=True;")); |             opts.UseSqlServer(configurationManager.GetConnectionString(databaseName))); | ||||||
|  |  | ||||||
|         services.AddScoped<IGenericReader>(serviceProvider => serviceProvider.GetRequiredService<DbContext>()); |         services.AddScoped<IGenericReader>(serviceProvider => serviceProvider.GetRequiredService<DbContext>()); | ||||||
|         services.AddScoped<IGenericWriter>(serviceProvider => serviceProvider.GetRequiredService<DbContext>()); |         services.AddScoped<IGenericWriter>(serviceProvider => serviceProvider.GetRequiredService<DbContext>()); | ||||||
| @@ -31,6 +40,9 @@ public static class DependencyInjection | |||||||
|         services.AddScoped<IVisaApplicationsRepository, VisaApplicationsRepository>(); |         services.AddScoped<IVisaApplicationsRepository, VisaApplicationsRepository>(); | ||||||
|         services.AddScoped<ICitiesRepository, CitiesRepository>(); |         services.AddScoped<ICitiesRepository, CitiesRepository>(); | ||||||
|         services.AddScoped<ICountriesRepository, CountriesRepository>(); |         services.AddScoped<ICountriesRepository, CountriesRepository>(); | ||||||
|  |         services.AddScoped<IUsersRepository, UsersRepository>(); | ||||||
|  |  | ||||||
|  |         services.AddSingleton<IDateTimeProvider, DateTimeProvider>(); | ||||||
|  |  | ||||||
|         return services; |         return services; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ | |||||||
|     </ItemGroup> |     </ItemGroup> | ||||||
|  |  | ||||||
|     <ItemGroup> |     <ItemGroup> | ||||||
|  |       <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" /> | ||||||
|       <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0-preview.7.24405.3" /> |       <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0-preview.7.24405.3" /> | ||||||
|       <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.0-preview.7.24405.3" /> |       <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.0-preview.7.24405.3" /> | ||||||
|       <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0-preview.7.24405.3" /> |       <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0-preview.7.24405.3" /> | ||||||
|   | |||||||
| @@ -0,0 +1,27 @@ | |||||||
|  | using ApplicationLayer.AuthServices.LoginService; | ||||||
|  | using ApplicationLayer.AuthServices.RegisterService; | ||||||
|  | using ApplicationLayer.AuthServices.Requests; | ||||||
|  | using Microsoft.AspNetCore.Identity.Data; | ||||||
|  | using Microsoft.AspNetCore.Mvc; | ||||||
|  |  | ||||||
|  | namespace SchengenVisaApi.Controllers | ||||||
|  | { | ||||||
|  |     [ApiController] | ||||||
|  |     [Route("auth")] | ||||||
|  |     public class UsersController(IRegisterService registerService, ILoginService loginService) : Controller | ||||||
|  |     { | ||||||
|  |         [HttpPost] | ||||||
|  |         public async Task<IActionResult> Register(RegisterApplicantRequest request, CancellationToken cancellationToken) | ||||||
|  |         { | ||||||
|  |             await registerService.Register(request, cancellationToken); | ||||||
|  |             return Created(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         [HttpGet] | ||||||
|  |         public async Task<IActionResult> Login(string email, string password, CancellationToken cancellationToken) | ||||||
|  |         { | ||||||
|  |             var result = await loginService.LoginAsync(new UserLoginRequest(email, password), cancellationToken); | ||||||
|  |             return Ok(result); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| using ApplicationLayer.VisaApplications.Handlers; | using ApplicationLayer.DataAccessingServices.VisaApplications.Handlers; | ||||||
| using ApplicationLayer.VisaApplications.Requests; | using ApplicationLayer.DataAccessingServices.VisaApplications.Requests; | ||||||
| using Microsoft.AspNetCore.Mvc; | using Microsoft.AspNetCore.Mvc; | ||||||
|  |  | ||||||
| namespace SchengenVisaApi.Controllers; | namespace SchengenVisaApi.Controllers; | ||||||
|   | |||||||
| @@ -1,6 +1,10 @@ | |||||||
| using System.Reflection; | using System.Reflection; | ||||||
|  | using System.Text; | ||||||
| using ApplicationLayer; | using ApplicationLayer; | ||||||
| using Infrastructure; | using Infrastructure; | ||||||
|  | using Infrastructure.Auth; | ||||||
|  | using Microsoft.AspNetCore.Authentication.JwtBearer; | ||||||
|  | using Microsoft.IdentityModel.Tokens; | ||||||
|  |  | ||||||
| namespace SchengenVisaApi; | namespace SchengenVisaApi; | ||||||
|  |  | ||||||
| @@ -8,21 +12,61 @@ namespace SchengenVisaApi; | |||||||
| public static class DependencyInjection | public static class DependencyInjection | ||||||
| { | { | ||||||
|     /// Add needed services |     /// Add needed services | ||||||
|     public static IServiceCollection RegisterServices(this IServiceCollection services) |     public static void RegisterServices(this WebApplicationBuilder builder) | ||||||
|     { |     { | ||||||
|         services |         var config = builder.Configuration; | ||||||
|             .AddInfrastructure() |         var environment = builder.Environment; | ||||||
|  |  | ||||||
|  |         builder.Services | ||||||
|  |             .AddInfrastructure(config, environment.IsDevelopment()) | ||||||
|             .AddApplicationLayer() |             .AddApplicationLayer() | ||||||
|             .AddPresentation(); |             .AddAuth(config) | ||||||
|  |             .AddPresentation(environment); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Add services needed for Presentation layer | ||||||
|  |     private static void AddPresentation(this IServiceCollection services, | ||||||
|  |         IWebHostEnvironment environment) | ||||||
|  |     { | ||||||
|  |         if (environment.IsDevelopment()) | ||||||
|  |         { | ||||||
|  |             services.AddSwagger(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         services.AddControllers(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Adds authentication, authorization and token generator | ||||||
|  |     private static IServiceCollection AddAuth(this IServiceCollection services, IConfigurationManager configurationManager) | ||||||
|  |     { | ||||||
|  |         var parameters = new TokenValidationParameters | ||||||
|  |         { | ||||||
|  |             ValidIssuer = configurationManager["JwtSettings:Issuer"], | ||||||
|  |             ValidAudience = configurationManager["JwtSettings:Audience"], | ||||||
|  |             IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configurationManager["JwtSettings:Key"]!)), | ||||||
|  |             ValidateIssuer = true, | ||||||
|  |             ValidateAudience = true, | ||||||
|  |             ValidateLifetime = true, | ||||||
|  |             ValidateIssuerSigningKey = true | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) | ||||||
|  |             .AddJwtBearer(opts => opts.TokenValidationParameters = parameters); | ||||||
|  |         services.AddAuthorization(); | ||||||
|  |  | ||||||
|  |         services.AddTokenGenerator(new TokenGeneratorOptions( | ||||||
|  |             Issuer: parameters.ValidIssuer!, | ||||||
|  |             Audience: parameters.ValidAudience!, | ||||||
|  |             Credentials: new SigningCredentials(parameters.IssuerSigningKey, SecurityAlgorithms.HmacSha256), | ||||||
|  |             ValidTime: TimeSpan.FromMinutes(30) | ||||||
|  |         )); | ||||||
|  |  | ||||||
|         return services; |         return services; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Add services needed for Presentation layer |     /// Add swagger | ||||||
|     private static void AddPresentation(this IServiceCollection services) |     private static void AddSwagger(this IServiceCollection services) | ||||||
|     { |     { | ||||||
|         services.AddControllers(); |  | ||||||
|         services.AddEndpointsApiExplorer(); |  | ||||||
|         services.AddSwaggerGen(options => |         services.AddSwaggerGen(options => | ||||||
|         { |         { | ||||||
|             var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; |             var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; | ||||||
|   | |||||||
| @@ -1,11 +1,12 @@ | |||||||
| namespace SchengenVisaApi; | namespace SchengenVisaApi; | ||||||
|  |  | ||||||
| #pragma warning disable CS1591 | #pragma warning disable CS1591 | ||||||
| public class Program | public class Program | ||||||
| { | { | ||||||
|     public static void Main(string[] args) |     public static void Main(string[] args) | ||||||
|     { |     { | ||||||
|         var builder = WebApplication.CreateBuilder(args); |         var builder = WebApplication.CreateBuilder(args); | ||||||
|         builder.Services.RegisterServices(); |         builder.RegisterServices(); | ||||||
|  |  | ||||||
|         var app = builder.Build(); |         var app = builder.Build(); | ||||||
|         app.ConfigurePipelineRequest(); |         app.ConfigurePipelineRequest(); | ||||||
| @@ -13,4 +14,4 @@ public class Program | |||||||
|         app.Run(); |         app.Run(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| #pragma warning restore CS1591 | #pragma warning restore CS1591 | ||||||
|   | |||||||
| @@ -11,8 +11,11 @@ public static class PipelineRequest | |||||||
|  |  | ||||||
|         app.UseHttpsRedirection(); |         app.UseHttpsRedirection(); | ||||||
|  |  | ||||||
|  |         app.UseAuthentication() | ||||||
|  |             .UseAuthorization(); | ||||||
|  |  | ||||||
|         app.MapControllers(); |         app.MapControllers(); | ||||||
|  |  | ||||||
|         return app; |         return app; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,5 +5,16 @@ | |||||||
|       "Microsoft.AspNetCore": "Warning" |       "Microsoft.AspNetCore": "Warning" | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "AllowedHosts": "*" |   "AllowedHosts": "*", | ||||||
|  |  | ||||||
|  |   "ConnectionStrings": { | ||||||
|  |     "developmentDB": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=visadb;Integrated Security=True;", | ||||||
|  |     "normal'naya db": "" | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   "JwtSettings": { | ||||||
|  |     "Issuer":"visaAPI", | ||||||
|  |     "Audience":"visaClient", | ||||||
|  |     "Key": "frsjiajfapojrpwauflakpiowaidoaplakrf" | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user