Skip to content

Commit f91d5bc

Browse files
Merge branch 'master' into ValidationForCreateDTOs
2 parents a6ad4a6 + bd52e2d commit f91d5bc

23 files changed

+540
-177
lines changed

CheckDrive.Api/CheckDrive.Api/Controllers/AccountsController.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
using CheckDrive.Application.DTOs.Account;
1+
using CheckDrive.Application.Constants;
2+
using CheckDrive.Application.DTOs.Account;
23
using CheckDrive.Application.Interfaces;
4+
using Microsoft.AspNetCore.Authorization;
35
using Microsoft.AspNetCore.Mvc;
46

57
namespace CheckDrive.Api.Controllers;
@@ -32,6 +34,7 @@ public async Task<ActionResult<AccountDto>> GetAccountByIdAsync(string id)
3234
}
3335

3436
[HttpPost]
37+
[Authorize(Roles = $"{Roles.Manager},{Roles.Administrator}")]
3538
public async Task<ActionResult<AccountDto>> CreateAsync([FromBody] CreateAccountDto account)
3639
{
3740
var createdAccount = await _service.CreateAsync(account);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using CheckDrive.Application.DTOs.Identity;
2+
using CheckDrive.Application.Interfaces.Auth;
3+
using Microsoft.AspNetCore.Mvc;
4+
5+
namespace CheckDrive.Api.Controllers.Auth;
6+
7+
[Route("api/auth")]
8+
[ApiController]
9+
public class AuthController : ControllerBase
10+
{
11+
private readonly IAuthService _authService;
12+
13+
public AuthController(IAuthService authService)
14+
{
15+
_authService = authService ?? throw new ArgumentNullException(nameof(authService));
16+
}
17+
18+
[HttpPost("login")]
19+
public async Task<IActionResult> LoginAsync([FromBody] LoginDto request)
20+
{
21+
var token = await _authService.LoginAsync(request);
22+
23+
return Ok(token);
24+
}
25+
}

CheckDrive.Api/CheckDrive.Api/Extensions/DependencyInjection.cs

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using Newtonsoft.Json.Converters;
1414
using Newtonsoft.Json.Serialization;
1515
using System.Text;
16+
using CheckDrive.Api.Filters;
1617

1718
namespace CheckDrive.Api.Extensions;
1819

@@ -172,3 +173,4 @@ public static void AddValidators(IServiceCollection services)
172173
services.AddValidatorsFromAssemblyContaining<CreateDispatcherReviewDtoValidator>();
173174
}
174175
}
176+

CheckDrive.Api/CheckDrive.Api/Extensions/StartupExtensions.cs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-

2-
3-
using CheckDrive.Api.Helpers;
1+
using CheckDrive.Api.Helpers;
42
using CheckDrive.Api.Middlewares;
3+
using CheckDrive.Application.Constants;
54
using CheckDrive.Domain.Interfaces;
65
using CheckDrive.TestDataCreator.Configurations;
76
using Microsoft.AspNetCore.Identity;

CheckDrive.Api/CheckDrive.Api/Helpers/CustomJsonFormatter.cs

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using Serilog.Formatting;
33
using System.Text.Json;
44

5+
namespace CheckDrive.Api.Helpers;
6+
57
public class CustomJsonFormatter : ITextFormatter
68
{
79
public void Format(LogEvent logEvent, TextWriter output)

CheckDrive.Api/CheckDrive.Api/Middlewares/ErrorHandlerMiddleware.cs

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Microsoft.AspNetCore.Http.HttpResults;
1+
using CheckDrive.Domain.Exceptions;
2+
using Microsoft.AspNetCore.Http.HttpResults;
23
using System.Net;
34

45
namespace CheckDrive.Api.Middlewares;
@@ -37,6 +38,18 @@ private async Task HandleAsync(HttpContext context, Exception exception)
3738
message = exception.Message;
3839
}
3940

41+
if (exception is InvalidLoginAttemptException)
42+
{
43+
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
44+
message = exception.Message;
45+
}
46+
47+
if (exception is RegistrationFailedException)
48+
{
49+
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
50+
message = exception.Message;
51+
}
52+
4053
await context.Response.WriteAsync(message);
4154
_logger.LogError($"{message}. {exception.Message}");
4255
}

CheckDrive.Api/CheckDrive.Api/Program.cs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using CheckDrive.Api.Extensions;
2+
using CheckDrive.Api.Helpers;
23
using Microsoft.AspNetCore.CookiePolicy;
34
using Serilog;
45

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace CheckDrive.Application.Constants;
2+
3+
public static class Roles
4+
{
5+
public const string Administrator = nameof(Administrator);
6+
public const string Driver = nameof(Driver);
7+
public const string Doctor = nameof(Doctor);
8+
public const string Manager = nameof(Manager);
9+
public const string Operator = nameof(Operator);
10+
public const string Mechanic = nameof(Mechanic);
11+
public const string Dispatcher = nameof(Dispatcher);
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
using System.ComponentModel.DataAnnotations;
2+
3+
namespace CheckDrive.Application.DTOs.Identity;
4+
5+
public sealed record LoginDto(
6+
string UserName,
7+
string Password);

CheckDrive.Api/CheckDrive.Application/Extensions/DependencyInjection.cs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
using CheckDrive.Application.Interfaces;
2-
using CheckDrive.Application.Interfaces.Review;
1+
using CheckDrive.Application.Interfaces.Review;
2+
using CheckDrive.Application.Interfaces;
33
using CheckDrive.Application.Services;
44
using CheckDrive.Application.Services.Review;
55
using Microsoft.Extensions.DependencyInjection;
6+
using CheckDrive.Application.Interfaces.Auth;
7+
using CheckDrive.Application.Services.Auth;
68

79
namespace CheckDrive.Application.Extensions;
810

@@ -11,14 +13,14 @@ public static class DependencyInjection
1113
public static IServiceCollection RegisterApplication(this IServiceCollection services)
1214
{
1315
AddServices(services);
14-
1516
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
1617

1718
return services;
1819
}
1920

2021
private static void AddServices(IServiceCollection services)
2122
{
23+
services.AddScoped<IAuthService, AuthService>();
2224
services.AddScoped<IDoctorReviewService, DoctorReviewService>();
2325
services.AddScoped<IMechanicHandoverService, MechanicHandoverService>();
2426
services.AddScoped<IOperatorReviewService, OperatorReviewService>();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using CheckDrive.Application.DTOs.Identity;
2+
3+
namespace CheckDrive.Application.Interfaces.Auth;
4+
5+
public interface IAuthService
6+
{
7+
Task<string> LoginAsync(LoginDto request);
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using Microsoft.AspNetCore.Identity;
2+
3+
namespace CheckDrive.Application.Interfaces.Auth;
4+
5+
public interface IJwtTokenGenerator
6+
{
7+
string GenerateToken(IdentityUser user, IList<string> roles);
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using CheckDrive.Application.DTOs.Identity;
2+
using CheckDrive.Application.Interfaces.Auth;
3+
using CheckDrive.Domain.Exceptions;
4+
using Microsoft.AspNetCore.Identity;
5+
6+
namespace CheckDrive.Application.Services.Auth;
7+
8+
internal sealed class AuthService : IAuthService
9+
{
10+
private readonly IJwtTokenGenerator _jwtTokenGenerator;
11+
private readonly UserManager<IdentityUser> _userManager;
12+
13+
public AuthService(IJwtTokenGenerator jwtTokenGenerator, UserManager<IdentityUser> userManager)
14+
{
15+
_jwtTokenGenerator = jwtTokenGenerator ?? throw new ArgumentNullException(nameof(jwtTokenGenerator));
16+
_userManager = userManager ?? throw new ArgumentNullException(nameof(userManager));
17+
}
18+
19+
public async Task<string> LoginAsync(LoginDto request)
20+
{
21+
ArgumentNullException.ThrowIfNull(request);
22+
23+
var user = await _userManager.FindByNameAsync(request.UserName);
24+
25+
if (user is null || !await _userManager.CheckPasswordAsync(user, request.Password))
26+
{
27+
throw new InvalidLoginAttemptException("Invalid email or password");
28+
}
29+
30+
var roles = await _userManager.GetRolesAsync(user);
31+
var token = _jwtTokenGenerator.GenerateToken(user, roles);
32+
33+
return token;
34+
}
35+
}

CheckDrive.Api/CheckDrive.Application/Services/Review/MechanicHandoverService.cs

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ private async Task<Mechanic> GetAndValidateMechanicAsync(int mechanicId)
5757
private async Task<CheckPoint> GetAndValidateCheckPointAsync(int checkPointId)
5858
{
5959
var checkPoint = await _context.CheckPoints
60+
.Include(x => x.DoctorReview)
6061
.FirstOrDefaultAsync(x => x.Id == checkPointId);
6162

6263
if (checkPoint == null)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace CheckDrive.Domain.Exceptions;
2+
3+
public class InvalidLoginAttemptException : Exception
4+
{
5+
public InvalidLoginAttemptException() : base() { }
6+
public InvalidLoginAttemptException(string message) : base(message) { }
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace CheckDrive.Domain.Exceptions;
2+
3+
public class RegistrationFailedException : Exception
4+
{
5+
public RegistrationFailedException() { }
6+
public RegistrationFailedException(string message) : base(message) { }
7+
}

CheckDrive.Api/CheckDrive.Infrastructure/Configurations/EmailConfiguration.cs

+15-15
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@ namespace CheckDrive.Infrastructure.Configurations;
55
public class EmailConfigurations
66
{
77
public const string SectionName = nameof(EmailConfigurations);
8-
9-
[Required]
8+
9+
[Required(ErrorMessage = "From is required")]
1010
[EmailAddress]
11-
public required string From { get; set; }
12-
13-
[Required]
14-
public required string Server { get; set; }
15-
16-
[Required]
17-
public required int Port { get; set; }
18-
19-
[Required]
20-
public required string UserName { get; set; }
21-
22-
[Required]
23-
public required string Password { get; set; }
11+
public required string From { get; init; }
12+
13+
[Required(ErrorMessage = "Server is required")]
14+
public required string Server { get; init; }
15+
16+
[Required(ErrorMessage = "Port is required")]
17+
public required int Port { get; init; }
18+
19+
[Required(ErrorMessage = "Username is required")]
20+
public required string UserName { get; init; }
21+
22+
[Required(ErrorMessage = "Password is required")]
23+
public required string Password { get; init; }
2424
}

CheckDrive.Api/CheckDrive.Infrastructure/Extensions/DependencyInjection.cs

+27-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
using CheckDrive.Application.Interfaces;
2-
using CheckDrive.Domain.Interfaces;
1+
using CheckDrive.Domain.Interfaces;
2+
using CheckDrive.Application.Interfaces;
33
using CheckDrive.Infrastructure.Configurations;
44
using CheckDrive.Infrastructure.Email;
55
using CheckDrive.Infrastructure.Persistence;
@@ -9,6 +9,8 @@
99
using Microsoft.EntityFrameworkCore;
1010
using Microsoft.Extensions.Configuration;
1111
using Microsoft.Extensions.DependencyInjection;
12+
using CheckDrive.Infrastructure.Helpers;
13+
using CheckDrive.Application.Interfaces.Auth;
1214

1315
namespace CheckDrive.Infrastructure.Extensions;
1416

@@ -18,8 +20,9 @@ public static IServiceCollection RegisterInfrastructure(this IServiceCollection
1820
{
1921
AddPersistence(services, configuration);
2022
AddConfigurations(services, configuration);
21-
AddFluentEmail(services, configuration);
23+
AddEmail(services, configuration);
2224
AddServices(services);
25+
AddIdentity(services);
2326

2427
return services;
2528
}
@@ -28,10 +31,6 @@ private static void AddPersistence(IServiceCollection services, IConfiguration c
2831
{
2932
services.AddDbContext<ICheckDriveDbContext, CheckDriveDbContext>(options =>
3033
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
31-
32-
services.AddIdentity<IdentityUser, IdentityRole>()
33-
.AddEntityFrameworkStores<CheckDriveDbContext>()
34-
.AddDefaultTokenProviders();
3534
}
3635

3736
private static void AddConfigurations(IServiceCollection services, IConfiguration configuration)
@@ -52,7 +51,7 @@ private static void AddConfigurations(IServiceCollection services, IConfiguratio
5251
.ValidateOnStart();
5352
}
5453

55-
private static void AddFluentEmail(IServiceCollection services, IConfiguration configuration)
54+
private static void AddEmail(IServiceCollection services, IConfiguration configuration)
5655
{
5756
var emailSettings = configuration
5857
.GetSection(EmailConfigurations.SectionName)
@@ -82,5 +81,25 @@ private static void AddServices(IServiceCollection services)
8281
{
8382
services.AddScoped<IEmailService, EmailService>();
8483
services.AddScoped<ISmsService, SmsService>();
84+
services.AddSingleton<IJwtTokenGenerator, JwtTokenGenerator>();
85+
}
86+
87+
private static void AddIdentity(IServiceCollection services)
88+
{
89+
services.AddIdentity<IdentityUser, IdentityRole>(options =>
90+
{
91+
options.Password.RequiredLength = 7;
92+
options.Password.RequireUppercase = false;
93+
options.Password.RequireLowercase = false;
94+
options.Password.RequireNonAlphanumeric = false;
95+
options.Password.RequireDigit = false;
96+
})
97+
.AddEntityFrameworkStores<CheckDriveDbContext>()
98+
.AddDefaultTokenProviders();
99+
100+
services.Configure<DataProtectionTokenProviderOptions>(options =>
101+
{
102+
options.TokenLifespan = TimeSpan.FromHours(12);
103+
});
85104
}
86105
}

0 commit comments

Comments
 (0)