feat: add account management functionality
This commit is contained in:
parent
5fecec2d3e
commit
287dccd440
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
*.Development.json
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
@ -35,6 +35,7 @@ public class MapperInitializer : Profile
|
||||
CreateMap<Address, InCityAddressDto>().ReverseMap();
|
||||
CreateMap<Address, CreateAddressInRouteAddress>().ReverseMap();
|
||||
CreateMap<Address, AddressInRouteAddress>().ReverseMap();
|
||||
CreateMap<Address, InTicketAddress>().ReverseMap();
|
||||
|
||||
CreateMap<RouteAddress, RouteAddressDto>()
|
||||
.ForMember(d => d.AddressName, opt => opt.MapFrom(src => src.Address.Name))
|
||||
@ -107,9 +108,6 @@ public class MapperInitializer : Profile
|
||||
CreateMap<User, StrippedUserDto>().ReverseMap();
|
||||
|
||||
CreateMap<User, DriverDto>().ForMember(d => d.CompanyId, o => o.MapFrom(s => s.Employer.CompanyId));
|
||||
CreateMap<UserDto, DriverDto>().ReverseMap();
|
||||
CreateMap<CreateUserDto, CreateDriverDto>().ReverseMap();
|
||||
CreateMap<CreateDriverDto, UpdateDriverDto>().ReverseMap();
|
||||
|
||||
CreateMap<RouteAddressDetails, RouteAddressDetailsInVehicleEnrollmentDto>().ReverseMap();
|
||||
CreateMap<RouteAddressDetails, CreateRouteAddressDetailsInVehicleEnrollmentDto>().ReverseMap();
|
||||
|
95
Server/Controllers/AccountController.cs
Normal file
95
Server/Controllers/AccountController.cs
Normal file
@ -0,0 +1,95 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Server.Services;
|
||||
using SharedModels.Requests;
|
||||
|
||||
namespace Server.Controllers;
|
||||
|
||||
[Route("api/account")]
|
||||
[ApiController]
|
||||
public class AccountController : ControllerBase
|
||||
{
|
||||
private readonly IAccountManagementService _accountManagementService;
|
||||
|
||||
public AccountController(IAccountManagementService accountManagementService)
|
||||
{
|
||||
_accountManagementService = accountManagementService;
|
||||
}
|
||||
|
||||
[HttpPost("changeInformation")]
|
||||
public async Task<IActionResult> ChangeInformation([FromBody] ChangeInformationRequest request)
|
||||
{
|
||||
var result = await _accountManagementService.ChangeInformation(request);
|
||||
|
||||
if (!result.isSucceed)
|
||||
{
|
||||
return result.actionResult;
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("changeEmail")]
|
||||
public async Task<IActionResult> ChangeEmail([FromBody] ChangeEmailRequest request)
|
||||
{
|
||||
var result = await _accountManagementService.ChangeEmail(request);
|
||||
|
||||
if (!result.isSucceed)
|
||||
{
|
||||
return result.actionResult;
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("confirmationEmailCallback")]
|
||||
public async Task<IActionResult> ConfirmChangeEmail([FromBody] ConfirmChangeEmailRequest request)
|
||||
{
|
||||
var result = await _accountManagementService.ConfirmChangeEmail(request);
|
||||
|
||||
if (!result.isSucceed)
|
||||
{
|
||||
return result.actionResult;
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("changePhoneNumber")]
|
||||
public async Task<IActionResult> ChangePhoneNumber([FromBody] ChangePhoneNumberRequest request)
|
||||
{
|
||||
var result = await _accountManagementService.ChangePhoneNumber(request);
|
||||
|
||||
if (!result.isSucceed)
|
||||
{
|
||||
return result.actionResult;
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("confirmPhoneNumber")]
|
||||
public async Task<IActionResult> ConfirmPhoneNumber([FromBody] ConfirmChangePhoneNumberRequest request)
|
||||
{
|
||||
var result = await _accountManagementService.ConfirmPhoneNumberChange(request);
|
||||
|
||||
if (!result.isSucceed)
|
||||
{
|
||||
return result.actionResult;
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("changePassword")]
|
||||
public async Task<IActionResult> ChangePassword([FromBody] ChangePasswordRequest request)
|
||||
{
|
||||
var result = await _accountManagementService.ChangePassword(request);
|
||||
|
||||
if (!result.isSucceed)
|
||||
{
|
||||
return result.actionResult;
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
@ -22,30 +22,42 @@ public class AuthenticationController : ControllerBase
|
||||
}
|
||||
|
||||
[HttpPost("register")]
|
||||
public async Task<IActionResult> RegisterAsync([FromBody] RegistrationRequest registerRequest)
|
||||
public async Task<IActionResult> RegisterAsync([FromBody] RegistrationRequest request)
|
||||
{
|
||||
var (succeeded, message) = await _authService.RegisterAsync(registerRequest);
|
||||
var result = await _authService.Register(request);
|
||||
|
||||
if (!succeeded)
|
||||
if (!result.succeeded)
|
||||
{
|
||||
return BadRequest(new ResponseBase {Message = message});
|
||||
return result.actionResult;
|
||||
}
|
||||
|
||||
return Ok(new ResponseBase{ Message = message });
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpGet("confirmEmail")]
|
||||
public async Task<IActionResult> ConfirmEmailAsync([FromQuery] string email, [FromQuery] string token,
|
||||
[FromQuery] string redirectionUrl)
|
||||
[HttpPost("confirmEmail")]
|
||||
public async Task<IActionResult> ConfirmEmail([FromBody] ConfirmRegistrationEmailRequest request)
|
||||
{
|
||||
var (succeeded, message) = await _authService.ConfirmEmailAsync(email, token);
|
||||
var result = await _authService.ConfirmRegistrationEmail(request);
|
||||
|
||||
if (!succeeded)
|
||||
if (!result.succeeded)
|
||||
{
|
||||
return BadRequest(new ResponseBase {Message = message});
|
||||
return result.actionResult;
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("confirmPhoneNumber")]
|
||||
public async Task<IActionResult> ConfirmPhoneNumber([FromBody] ConfirmRegistrationPhoneNumberRequest request)
|
||||
{
|
||||
var result = await _authService.ConfirmRegistrationPhoneNumber(request);
|
||||
|
||||
return Redirect(redirectionUrl);
|
||||
if (!result.succeeded)
|
||||
{
|
||||
return result.actionResult;
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("authenticate")]
|
||||
|
@ -30,8 +30,7 @@ public class SeedData
|
||||
var adminUser = new User
|
||||
{
|
||||
FirstName = "user", LastName = "user", Patronymic = "user",
|
||||
Email = "admin@autobus.com",
|
||||
EmailConfirmed = true,
|
||||
Email = "admin@autobus.com", EmailConfirmed = true
|
||||
};
|
||||
|
||||
await userManager.CreateAsync(adminUser, Identity.DefaultPassword);
|
||||
@ -41,7 +40,7 @@ public class SeedData
|
||||
|
||||
companyUser = new User
|
||||
{
|
||||
FirstName = "user", LastName = "user", Patronymic = "user",
|
||||
FirstName = "Firstname", LastName = "Lastname", Patronymic = "Patronymic",
|
||||
Email = "company@autobus.com",
|
||||
EmailConfirmed = true
|
||||
};
|
||||
@ -54,8 +53,11 @@ public class SeedData
|
||||
driverUser = new User
|
||||
{
|
||||
FirstName = "user", LastName = "user", Patronymic = "user",
|
||||
Email = "driver@autobus.com",
|
||||
EmailConfirmed = true
|
||||
Email = "driver@autobus.com", EmailConfirmed = true,
|
||||
PhoneNumber = "+380951935723", PhoneNumberConfirmed = true,
|
||||
BirthDate = DateTime.Now.ToUniversalTime(),
|
||||
Document = Identity.Document.Passport, DocumentDetails = "Some details",
|
||||
Gender = Identity.Gender.Male
|
||||
};
|
||||
|
||||
await userManager.CreateAsync(driverUser, Identity.DefaultPassword);
|
||||
@ -66,8 +68,7 @@ public class SeedData
|
||||
var defaultUser = new User
|
||||
{
|
||||
FirstName = "user", LastName = "user", Patronymic = "user",
|
||||
Email = "user@autobus.com",
|
||||
EmailConfirmed = true
|
||||
Email = "user@autobus.com", EmailConfirmed = true
|
||||
};
|
||||
|
||||
await userManager.CreateAsync(defaultUser, Identity.DefaultPassword);
|
||||
|
@ -107,6 +107,7 @@ services.AddAutoMapper(typeof(MapperInitializer));
|
||||
|
||||
services.AddScoped<IEmailSenderService, EmailSenderService>();
|
||||
services.AddScoped<IAuthenticationService, AuthenticationService>();
|
||||
services.AddScoped<IAccountManagementService, AccountManagementService>();
|
||||
|
||||
services.AddScoped<ISessionUserService, SessionUserService>();
|
||||
|
||||
|
130
Server/Services/AccountManagementService.cs
Normal file
130
Server/Services/AccountManagementService.cs
Normal file
@ -0,0 +1,130 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Server.Models;
|
||||
using SharedModels.Requests;
|
||||
|
||||
namespace Server.Services;
|
||||
|
||||
public class AccountManagementService : IAccountManagementService
|
||||
{
|
||||
private readonly IEmailSenderService _emailSender;
|
||||
private readonly UserManager<User> _userManager;
|
||||
private readonly ISessionUserService _sessionUserService;
|
||||
|
||||
public AccountManagementService(IEmailSenderService emailSender, UserManager<User> userManager,
|
||||
ISessionUserService sessionUserService)
|
||||
{
|
||||
_emailSender = emailSender;
|
||||
_userManager = userManager;
|
||||
_sessionUserService = sessionUserService;
|
||||
|
||||
_userManager.UserValidators.Clear();
|
||||
}
|
||||
|
||||
public async Task<(bool isSucceed, IActionResult actionResult)> ChangeInformation(ChangeInformationRequest request)
|
||||
{
|
||||
var dbUser = await _userManager.FindByIdAsync(_sessionUserService.GetAuthUserId());
|
||||
|
||||
dbUser.FirstName = request.FistName;
|
||||
dbUser.LastName = request.LastName;
|
||||
dbUser.Patronymic = request.Patronymic;
|
||||
dbUser.BirthDate = new DateTime(request.BirthDate.Year, request.BirthDate.Month, request.BirthDate.Day, 0, 0, 0, DateTimeKind.Utc);
|
||||
dbUser.Gender = request.Gender;
|
||||
|
||||
await _userManager.UpdateAsync(dbUser);
|
||||
|
||||
return (true, null!);
|
||||
}
|
||||
|
||||
public async Task<(bool isSucceed, IActionResult actionResult)> ChangeEmail(ChangeEmailRequest request)
|
||||
{
|
||||
var dbUser = await _userManager.FindByIdAsync(_sessionUserService.GetAuthUserId());
|
||||
|
||||
if (dbUser.Email.ToLower() == request.NewEmail.ToLower())
|
||||
{
|
||||
return (false, new BadRequestObjectResult("You must specify a new email"));
|
||||
}
|
||||
|
||||
var changeEmailToken = await _userManager.GenerateChangeEmailTokenAsync(dbUser, request.NewEmail);
|
||||
|
||||
var securityMessage =
|
||||
$"Someone is trying to change email address of your account to {request.NewEmail}. " +
|
||||
"If it is not you please follow account recovery procedure.";
|
||||
var confirmationMessage =
|
||||
"Someone changed account email to your address.\n" +
|
||||
$"Here is your confirmation code: {changeEmailToken}\n\n" +
|
||||
"If this was not you, please ignore this message.";
|
||||
|
||||
try { await _emailSender.SendMail(dbUser.Email, "Security alert", securityMessage); }
|
||||
catch (Exception) { /* ignored */ }
|
||||
|
||||
try { await _emailSender.SendMail(request.NewEmail, "Change email confirmation", confirmationMessage); }
|
||||
catch (Exception) { /* ignored */ }
|
||||
|
||||
return (true, null!);
|
||||
}
|
||||
|
||||
public async Task<(bool isSucceed, IActionResult actionResult)> ConfirmChangeEmail(ConfirmChangeEmailRequest request)
|
||||
{
|
||||
var dbUser = await _userManager.FindByIdAsync(_sessionUserService.GetAuthUserId());
|
||||
|
||||
var result = await _userManager.ChangeEmailAsync(dbUser, request.NewEmail, request.Token);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return (false, new BadRequestObjectResult($"Error confirming email change {request.NewEmail}"));
|
||||
}
|
||||
|
||||
return (true, null!);
|
||||
}
|
||||
|
||||
public async Task<(bool isSucceed, IActionResult actionResult)> ChangePhoneNumber(ChangePhoneNumberRequest request)
|
||||
{
|
||||
var dbUser = await _userManager.FindByIdAsync(_sessionUserService.GetAuthUserId());
|
||||
|
||||
var changePhoneNumberToken = await _userManager.GenerateChangePhoneNumberTokenAsync(dbUser, request.PhoneNumber);
|
||||
|
||||
var securityMessage =
|
||||
$"Someone is trying to change phone number of your account to {request.PhoneNumber}. " +
|
||||
"If it is not you please follow account recovery procedure.";
|
||||
|
||||
try { await _emailSender.SendMail(dbUser.Email, "Security alert", securityMessage); }
|
||||
catch (Exception) { /* ignored */ }
|
||||
|
||||
// TODO: Send sms message to new phone number
|
||||
|
||||
return (true, null!);
|
||||
}
|
||||
|
||||
public async Task<(bool isSucceed, IActionResult actionResult)> ConfirmPhoneNumberChange(ConfirmChangePhoneNumberRequest request)
|
||||
{
|
||||
var dbUser = await _userManager.FindByIdAsync(_sessionUserService.GetAuthUserId());
|
||||
|
||||
var result = await _userManager.ChangePhoneNumberAsync(dbUser, request.PhoneNumber, request.Token);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return (false, new BadRequestObjectResult(result.Errors));
|
||||
}
|
||||
|
||||
return (true, null!);
|
||||
}
|
||||
|
||||
public async Task<(bool isSucceed, IActionResult actionResult)> ChangePassword(ChangePasswordRequest request)
|
||||
{
|
||||
var dbUser = await _userManager.FindByIdAsync(_sessionUserService.GetAuthUserId());
|
||||
|
||||
if (!await _userManager.CheckPasswordAsync(dbUser, request.CurrentPassword))
|
||||
{
|
||||
return (false, new BadRequestObjectResult("Invalid current password"));
|
||||
}
|
||||
|
||||
await _userManager.ChangePasswordAsync(dbUser, request.CurrentPassword, request.NewPassword);
|
||||
|
||||
var securityMessage = "Someone is changed your account password." +
|
||||
"If this was not you please follow account recovery procedure.";
|
||||
|
||||
try { await _emailSender.SendMail(dbUser.Email, "Security alert", securityMessage); }
|
||||
catch (Exception) { /* ignored */ }
|
||||
|
||||
return (true, null!);
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Google.Apis.Auth;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
@ -22,114 +23,104 @@ public class AuthenticationService : IAuthenticationService
|
||||
private readonly Jwt _jwt;
|
||||
|
||||
private readonly UserManager<User> _userManager;
|
||||
private readonly RoleManager<IdentityRole> _roleManager;
|
||||
private readonly IHttpContextAccessor _contextAccessor;
|
||||
private readonly LinkGenerator _linkGenerator;
|
||||
private readonly IEmailSenderService _emailSender;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public AuthenticationService(UserManager<User> userManager,
|
||||
RoleManager<IdentityRole> roleManager, IOptions<Jwt> jwt,
|
||||
IHttpContextAccessor contextAccessor, LinkGenerator linkGenerator,
|
||||
public AuthenticationService(UserManager<User> userManager, IOptions<Jwt> jwt,
|
||||
IEmailSenderService emailSender, IConfiguration configuration)
|
||||
{
|
||||
_jwt = jwt.Value;
|
||||
|
||||
_userManager = userManager;
|
||||
_roleManager = roleManager;
|
||||
_contextAccessor = contextAccessor;
|
||||
_linkGenerator = linkGenerator;
|
||||
_emailSender = emailSender;
|
||||
_configuration = configuration;
|
||||
|
||||
_userManager.UserValidators.Clear();
|
||||
}
|
||||
|
||||
public async Task<(bool succeeded, string message)> RegisterAsync(RegistrationRequest regRequest)
|
||||
public async Task<(bool succeeded, IActionResult actionResult)> Register(RegistrationRequest request)
|
||||
{
|
||||
var userWithSameEmail = await _userManager.FindByEmailAsync(regRequest.Email);
|
||||
var userWithSameEmail = await _userManager.FindByEmailAsync(request.Email);
|
||||
if (userWithSameEmail != null)
|
||||
{
|
||||
return (false, "Email is already registered.");
|
||||
return (false, new BadRequestObjectResult("Email is already registered."));
|
||||
}
|
||||
|
||||
var userWithSamePhone = await _userManager.Users
|
||||
.SingleOrDefaultAsync(u => u.PhoneNumber == regRequest.PhoneNumber);
|
||||
.SingleOrDefaultAsync(u => u.PhoneNumber == request.PhoneNumber);
|
||||
if (userWithSamePhone != null)
|
||||
{
|
||||
return (false, "Phone is already registered.");
|
||||
return (false, new BadRequestObjectResult("Phone is already registered."));
|
||||
}
|
||||
|
||||
var user = new User
|
||||
{
|
||||
UserName = "temp",
|
||||
FirstName = regRequest.FirstName,
|
||||
LastName = regRequest.LastName,
|
||||
Patronymic = regRequest.Patronymic,
|
||||
Email = regRequest.Email,
|
||||
PhoneNumber = regRequest.PhoneNumber
|
||||
FirstName = request.FirstName,
|
||||
LastName = request.LastName,
|
||||
Patronymic = request.Patronymic,
|
||||
Email = request.Email,
|
||||
PhoneNumber = request.PhoneNumber
|
||||
};
|
||||
|
||||
var createUserResult = await _userManager.CreateAsync(user, regRequest.Password);
|
||||
var createUserResult = await _userManager.CreateAsync(user, request.Password);
|
||||
if (!createUserResult.Succeeded)
|
||||
{
|
||||
return (false, $"{createUserResult.Errors?.First().Description}");
|
||||
return (false, new BadRequestObjectResult(createUserResult.Errors));
|
||||
}
|
||||
|
||||
await _userManager.AddToRoleAsync(user, Identity.DefaultRole.ToString());
|
||||
|
||||
var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
var confirmationLink = _linkGenerator.GetUriByAction(_contextAccessor.HttpContext,
|
||||
"confirmEmail", "authentication",
|
||||
new { email = user.Email, token = token, redirectionUrl = regRequest.EmailConfirmationRedirectUrl },
|
||||
_contextAccessor.HttpContext.Request.Scheme);
|
||||
var emailConfirmationToken = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
var confirmationMessage =
|
||||
"Someone registered an account on our service with your email.\n" +
|
||||
$"Here is your confirmation code: {emailConfirmationToken}\n\n" +
|
||||
"If this was not you, please ignore this message.";
|
||||
|
||||
try { await _emailSender.SendMail(user.Email, "Email confirmation", confirmationMessage); }
|
||||
catch (Exception e) { /* ignored */ }
|
||||
|
||||
// TODO: Add phone number confirmation
|
||||
|
||||
try
|
||||
{
|
||||
await _emailSender.SendMail(user.Email, "Email confirmation", confirmationLink);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
return (true, $"User registered with email {user.Email}. Before signing in confirm your email" +
|
||||
$"by following a link sent to registered email address.");
|
||||
return (true, null!);
|
||||
}
|
||||
|
||||
public async Task<(bool succeeded, string? message)> ConfirmEmailAsync(string email, string token)
|
||||
public async Task<(bool succeeded, IActionResult actionResult)> ConfirmRegistrationEmail(ConfirmRegistrationEmailRequest request)
|
||||
{
|
||||
var user = await _userManager.FindByEmailAsync(email);
|
||||
if (user == null)
|
||||
{
|
||||
return (false, $"Email {email} not registered");
|
||||
}
|
||||
var dbUser = await _userManager.FindByEmailAsync(request.Email);
|
||||
|
||||
var result = await _userManager.ConfirmEmailAsync(user, token);
|
||||
var result = await _userManager.ConfirmEmailAsync(dbUser, request.Token);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return (false, $"Error confirming email {email} with token {token}");
|
||||
return (false, new BadRequestObjectResult($"Error confirming email."));
|
||||
}
|
||||
|
||||
return (true, $"Email {email} confirmed");
|
||||
return (true, null!);
|
||||
}
|
||||
|
||||
public async Task<(bool succeeded, IActionResult actionResult)> ConfirmRegistrationPhoneNumber(ConfirmRegistrationPhoneNumberRequest numberRequest)
|
||||
{
|
||||
var dbUser = await _userManager.Users.FirstAsync(u => u.PhoneNumber == numberRequest.PhoneNumber);
|
||||
|
||||
// TODO: Add phone number confirmation token validation
|
||||
|
||||
return (true, null!);
|
||||
}
|
||||
|
||||
public async Task<(bool succeeded, AuthenticationResponse authResponse, string? refreshToken)>
|
||||
AuthenticateAsync(AuthenticationRequest authRequest)
|
||||
AuthenticateAsync(AuthenticationRequest request)
|
||||
{
|
||||
var authResponse = new AuthenticationResponse();
|
||||
|
||||
User user;
|
||||
|
||||
user = await _userManager.FindByEmailAsync(authRequest.Email);
|
||||
var user = await _userManager.FindByEmailAsync(request.Email);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
authResponse.Message = $"No accounts registered with {authRequest.Email}.";
|
||||
authResponse.Message = $"No accounts registered with {request.Email}.";
|
||||
return (false, authResponse, null);
|
||||
}
|
||||
|
||||
if (!await _userManager.CheckPasswordAsync(user, authRequest.Password))
|
||||
if (!await _userManager.CheckPasswordAsync(user, request.Password))
|
||||
{
|
||||
authResponse.Message = $"Incorrect email or password.";
|
||||
return (false, authResponse, null);
|
||||
@ -160,13 +151,13 @@ public class AuthenticationService : IAuthenticationService
|
||||
}
|
||||
|
||||
public async Task<(bool succeeded, AuthenticationResponse authResponse, string? refreshToken)>
|
||||
AuthenticateWithGoogleAsync(GoogleAuthenticationRequest authRequest)
|
||||
AuthenticateWithGoogleAsync(GoogleAuthenticationRequest request)
|
||||
{
|
||||
GoogleJsonWebSignature.ValidationSettings settings = new GoogleJsonWebSignature.ValidationSettings();
|
||||
|
||||
settings.Audience = new List<string> { _configuration["Authentication:Google:ClientId"] };
|
||||
|
||||
GoogleJsonWebSignature.Payload payload = GoogleJsonWebSignature.ValidateAsync(authRequest.IdToken, settings).Result;
|
||||
GoogleJsonWebSignature.Payload payload = GoogleJsonWebSignature.ValidateAsync(request.IdToken, settings).Result;
|
||||
|
||||
var authResponse = new AuthenticationResponse();
|
||||
|
||||
@ -209,8 +200,8 @@ public class AuthenticationService : IAuthenticationService
|
||||
return (true, authResponse, refreshTokenString);
|
||||
}
|
||||
|
||||
public async Task<(bool succeeded, AuthenticationResponse authResponse,
|
||||
string? refreshToken)> RenewRefreshTokenAsync(string? token)
|
||||
public async Task<(bool succeeded, AuthenticationResponse authResponse, string? refreshToken)>
|
||||
RenewRefreshTokenAsync(string? token)
|
||||
{
|
||||
var authResponse = new AuthenticationResponse();
|
||||
|
||||
|
@ -22,6 +22,11 @@ public class EmailSenderService : IEmailSenderService
|
||||
_smtpClient = new SmtpClient();
|
||||
_smtpClient.SslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13;
|
||||
}
|
||||
|
||||
~EmailSenderService()
|
||||
{
|
||||
_smtpClient.Dispose();
|
||||
}
|
||||
|
||||
public async Task<(bool succeeded, string message)> SendMail(string toEmail, string subject, string message)
|
||||
{
|
||||
@ -38,7 +43,6 @@ public class EmailSenderService : IEmailSenderService
|
||||
await _smtpClient.AuthenticateAsync(Encoding.ASCII, _smtpCredentials.User, _smtpCredentials.Password);
|
||||
await _smtpClient.SendAsync(mailMessage);
|
||||
await _smtpClient.DisconnectAsync(true);
|
||||
_smtpClient.Dispose();
|
||||
|
||||
return (true, "Letter has been sent successfully");
|
||||
}
|
||||
|
19
Server/Services/IAccountManagementService.cs
Normal file
19
Server/Services/IAccountManagementService.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SharedModels.Requests;
|
||||
|
||||
namespace Server.Services;
|
||||
|
||||
public interface IAccountManagementService
|
||||
{
|
||||
Task<(bool isSucceed, IActionResult actionResult)> ChangeInformation(ChangeInformationRequest request);
|
||||
|
||||
Task<(bool isSucceed, IActionResult actionResult)> ChangeEmail(ChangeEmailRequest request);
|
||||
|
||||
Task<(bool isSucceed, IActionResult actionResult)> ConfirmChangeEmail(ConfirmChangeEmailRequest request);
|
||||
|
||||
Task<(bool isSucceed, IActionResult actionResult)> ChangePhoneNumber(ChangePhoneNumberRequest request);
|
||||
|
||||
Task<(bool isSucceed, IActionResult actionResult)> ConfirmPhoneNumberChange(ConfirmChangePhoneNumberRequest request);
|
||||
|
||||
Task<(bool isSucceed, IActionResult actionResult)> ChangePassword(ChangePasswordRequest request);
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SharedModels.Requests;
|
||||
using SharedModels.Responses;
|
||||
|
||||
@ -5,15 +6,17 @@ namespace Server.Services;
|
||||
|
||||
public interface IAuthenticationService
|
||||
{
|
||||
Task<(bool succeeded, string message)> RegisterAsync(RegistrationRequest regRequest);
|
||||
Task<(bool succeeded, IActionResult actionResult)> Register(RegistrationRequest request);
|
||||
|
||||
Task<(bool succeeded, IActionResult actionResult)> ConfirmRegistrationEmail(ConfirmRegistrationEmailRequest request);
|
||||
|
||||
Task<(bool succeeded, IActionResult actionResult)> ConfirmRegistrationPhoneNumber(ConfirmRegistrationPhoneNumberRequest numberRequest);
|
||||
|
||||
Task<(bool succeeded, string message)> ConfirmEmailAsync(string email, string token);
|
||||
Task<(bool succeeded, AuthenticationResponse authResponse, string? refreshToken)>
|
||||
AuthenticateAsync(AuthenticationRequest request);
|
||||
|
||||
Task<(bool succeeded, AuthenticationResponse authResponse, string? refreshToken)>
|
||||
AuthenticateAsync(AuthenticationRequest authRequest);
|
||||
|
||||
Task<(bool succeeded, AuthenticationResponse authResponse, string? refreshToken)>
|
||||
AuthenticateWithGoogleAsync(GoogleAuthenticationRequest authRequest);
|
||||
AuthenticateWithGoogleAsync(GoogleAuthenticationRequest request);
|
||||
|
||||
Task<(bool succeeded, AuthenticationResponse authResponse, string? refreshToken)>
|
||||
RenewRefreshTokenAsync(string? token);
|
||||
|
@ -10,6 +10,5 @@ public interface ISessionUserService
|
||||
public Task<bool> IsAuthUserCompanyVehicleEnrollment(int enrollmentId);
|
||||
public Task<bool> IsAuthUserCompanyDriver(string driverId);
|
||||
|
||||
|
||||
public Task<bool> IsAuthUserReview(int reviewId);
|
||||
}
|
@ -8,14 +8,15 @@ namespace Server.Services;
|
||||
|
||||
public interface IUserManagementService
|
||||
{
|
||||
Task<(bool isSucceeded, IActionResult? actionResult, UserDto user)> AddUser(CreateUserDto createUserDto);
|
||||
Task<(bool isSucceeded, IActionResult actionResult, UserDto user)> AddUser(CreateUserDto createUserDto);
|
||||
|
||||
Task<(bool isSucceeded, IActionResult? actionResult, IEnumerable<ExpandoObject> users, PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||
Task<(bool isSucceeded, IActionResult actionResult, IEnumerable<ExpandoObject> users, PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||
GetUsers(UserParameters parameters);
|
||||
|
||||
Task<(bool isSucceeded, IActionResult actionResult, ExpandoObject user)> GetUser(string id, string? fields);
|
||||
|
||||
Task<(bool isSucceeded, IActionResult actionResult, UserDto user)>
|
||||
UpdateUser(string id, UpdateUserDto updateUserDto);
|
||||
|
||||
Task<(bool isSucceeded, IActionResult? actionResult, ExpandoObject user)> GetUser(string id, string? fields);
|
||||
|
||||
Task<(bool isSucceeded, IActionResult? actionResult, UserDto user)> UpdateUser(string id, UpdateUserDto updateUserDto);
|
||||
|
||||
Task<(bool isSucceed, IActionResult? actionResult)> DeleteUser(string id);
|
||||
Task<(bool isSucceed, IActionResult actionResult)> DeleteUser(string id);
|
||||
}
|
@ -9,6 +9,7 @@ using Server.Models;
|
||||
using SharedModels.DataTransferObjects.Model;
|
||||
using SharedModels.QueryParameters;
|
||||
using SharedModels.QueryParameters.Objects;
|
||||
using SharedModels.Requests;
|
||||
|
||||
namespace Server.Services;
|
||||
|
||||
@ -21,9 +22,9 @@ public class UserManagementService : IUserManagementService
|
||||
private readonly IDataShaper<UserDto> _userDataShaper;
|
||||
private readonly IPager<ExpandoObject> _pager;
|
||||
|
||||
public UserManagementService(IMapper mapper, UserManager<User> userManager, RoleManager<IdentityRole> roleManager,
|
||||
ISortHelper<ExpandoObject> userSortHelper, IDataShaper<UserDto> userDataShaper, IPager<ExpandoObject> pager,
|
||||
ApplicationDbContext dbContext)
|
||||
public UserManagementService(IMapper mapper, UserManager<User> userManager,
|
||||
RoleManager<IdentityRole> roleManager, ISortHelper<ExpandoObject> userSortHelper,
|
||||
IDataShaper<UserDto> userDataShaper, IPager<ExpandoObject> pager)
|
||||
{
|
||||
_mapper = mapper;
|
||||
_userManager = userManager;
|
||||
@ -35,7 +36,8 @@ public class UserManagementService : IUserManagementService
|
||||
_userManager.UserValidators.Clear();
|
||||
}
|
||||
|
||||
public async Task<(bool isSucceeded, IActionResult? actionResult, UserDto user)> AddUser(CreateUserDto createUserDto)
|
||||
public async Task<(bool isSucceeded, IActionResult actionResult, UserDto user)>
|
||||
AddUser(CreateUserDto createUserDto)
|
||||
{
|
||||
var user = _mapper.Map<User>(createUserDto);
|
||||
user.BirthDate = user.BirthDate == null ? null : new DateTime(user.BirthDate.Value.Ticks, DateTimeKind.Utc);
|
||||
@ -45,12 +47,12 @@ public class UserManagementService : IUserManagementService
|
||||
|
||||
if (await _userManager.FindByEmailAsync(user.Email) != null)
|
||||
{
|
||||
return (false, new BadRequestObjectResult("Email already registered"), null);
|
||||
return (false, new BadRequestObjectResult("Email already registered"), null!);
|
||||
}
|
||||
|
||||
if (user.PhoneNumber != null && await _userManager.Users.FirstOrDefaultAsync(u => u.PhoneNumber == user.PhoneNumber) != null)
|
||||
{
|
||||
return (false, new BadRequestObjectResult("Phone number already registered"), null);
|
||||
return (false, new BadRequestObjectResult("Phone number already registered"), null!);
|
||||
}
|
||||
|
||||
if (createUserDto.Roles != null!)
|
||||
@ -59,7 +61,7 @@ public class UserManagementService : IUserManagementService
|
||||
{
|
||||
if (!await _roleManager.RoleExistsAsync(role))
|
||||
{
|
||||
return (false, new BadRequestObjectResult($"Roles \"{role}\" doesn't exist"), null);
|
||||
return (false, new BadRequestObjectResult($"Roles \"{role}\" doesn't exist"), null!);
|
||||
}
|
||||
|
||||
userDto.Roles.Add(role);
|
||||
@ -71,11 +73,11 @@ public class UserManagementService : IUserManagementService
|
||||
|
||||
userDto.Id = user.Id;
|
||||
|
||||
return (true, null, userDto);
|
||||
return (true, null!, userDto);
|
||||
}
|
||||
|
||||
public async Task<(bool isSucceeded, IActionResult? actionResult, IEnumerable<ExpandoObject> users,
|
||||
PagingMetadata<ExpandoObject> pagingMetadata)> GetUsers(UserParameters parameters)
|
||||
public async Task<(bool isSucceeded, IActionResult actionResult, IEnumerable<ExpandoObject> users, PagingMetadata<ExpandoObject> pagingMetadata)>
|
||||
GetUsers(UserParameters parameters)
|
||||
{
|
||||
var dbUsers = _userManager.Users.Include(u => u.Company)
|
||||
.Include(u => u.Reviews).Include(u => u.TicketGroups)
|
||||
@ -108,7 +110,7 @@ public class UserManagementService : IUserManagementService
|
||||
}
|
||||
}
|
||||
|
||||
return (true, null, shapedData, pagingMetadata);
|
||||
return (true, null!, shapedData, pagingMetadata);
|
||||
|
||||
void SearchByAllUserFields(ref IQueryable<User> users, string? search)
|
||||
{
|
||||
@ -126,7 +128,7 @@ public class UserManagementService : IUserManagementService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool isSucceeded, IActionResult? actionResult, ExpandoObject user)>
|
||||
public async Task<(bool isSucceeded, IActionResult actionResult, ExpandoObject user)>
|
||||
GetUser(string id, string? fields)
|
||||
{
|
||||
var dbUser = await _userManager.Users.Include(u => u.Employer).
|
||||
@ -142,24 +144,24 @@ public class UserManagementService : IUserManagementService
|
||||
fields = UserParameters.DefaultFields;
|
||||
}
|
||||
|
||||
var userDto = _mapper.Map<DriverDto>(dbUser);
|
||||
var userDto = _mapper.Map<UserDto>(dbUser);
|
||||
var shapedData = _userDataShaper.ShapeData(userDto, fields);
|
||||
|
||||
return (true, null, shapedData);
|
||||
return (true, null!, shapedData);
|
||||
}
|
||||
|
||||
public async Task<(bool isSucceeded, IActionResult? actionResult, UserDto user)>
|
||||
public async Task<(bool isSucceeded, IActionResult actionResult, UserDto user)>
|
||||
UpdateUser(string id, UpdateUserDto updateUserDto)
|
||||
{
|
||||
if (id != updateUserDto.Id)
|
||||
{
|
||||
return (false, new BadRequestObjectResult("Object and query ids don't match"), null);
|
||||
return (false, new BadRequestObjectResult("Object and query ids don't match"), null!);
|
||||
}
|
||||
|
||||
if (!await _userManager.Users.AnyAsync(u => u.Id == id))
|
||||
{
|
||||
|
||||
return (false, new NotFoundResult(), null);
|
||||
return (false, new NotFoundResult(), null!);
|
||||
}
|
||||
|
||||
var dbUser = await _userManager.FindByIdAsync(id);
|
||||
@ -198,7 +200,7 @@ public class UserManagementService : IUserManagementService
|
||||
{
|
||||
if (!await _roleManager.RoleExistsAsync(role))
|
||||
{
|
||||
return (false, new BadRequestObjectResult($"Roles \"{role}\" doesn't exist"), null);
|
||||
return (false, new BadRequestObjectResult($"Roles \"{role}\" doesn't exist"), null!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,10 +214,10 @@ public class UserManagementService : IUserManagementService
|
||||
|
||||
await _userManager.UpdateAsync(dbUser);
|
||||
|
||||
return (true, null, userDto);
|
||||
return (true, null!, userDto);
|
||||
}
|
||||
|
||||
public async Task<(bool isSucceed, IActionResult? actionResult)> DeleteUser(string id)
|
||||
public async Task<(bool isSucceed, IActionResult actionResult)> DeleteUser(string id)
|
||||
{
|
||||
var dbUser = await _userManager.FindByIdAsync(id);
|
||||
|
||||
@ -226,6 +228,6 @@ public class UserManagementService : IUserManagementService
|
||||
|
||||
await _userManager.DeleteAsync(dbUser);
|
||||
|
||||
return (true, null);
|
||||
return (true, null!);
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "host=localhost;database=auto.bus;user id=postgres;password=postgres;Include Error Detail = true"
|
||||
},
|
||||
"ApplicationName": "auto.bus",
|
||||
"SmtpCredentials": {
|
||||
"Host": "",
|
||||
"Port": 587,
|
||||
"User": "",
|
||||
"Password": ""
|
||||
},
|
||||
"Authentication": {
|
||||
"Google": {
|
||||
"ClientId": "",
|
||||
"ClientSecret": ""
|
||||
}
|
||||
},
|
||||
"Jwt": {
|
||||
"Key": "Secret which will never be exposed",
|
||||
"Audience": "Application URL",
|
||||
"Issuer": "Application URL",
|
||||
"ValidityInMinutes": 60,
|
||||
"RefreshTokenValidityInDays": 10
|
||||
}
|
||||
}
|
@ -7,10 +7,10 @@
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "host=localhost;database=auto.bus;user id=postgres;password=postgres;Include Error Detail = true"
|
||||
"DefaultConnection": ""
|
||||
},
|
||||
"UseApiExplorer": true,
|
||||
"ApplicationName": "auto.bus",
|
||||
"UseApiExplorer": false,
|
||||
"ApplicationName": "",
|
||||
"SmtpCredentials": {
|
||||
"Host": "",
|
||||
"Port": 587,
|
||||
@ -24,10 +24,10 @@
|
||||
}
|
||||
},
|
||||
"Jwt": {
|
||||
"Key": "Secret which will never be exposed",
|
||||
"Audience": "Application URL",
|
||||
"Issuer": "Application URL",
|
||||
"ValidityInMinutes": 1,
|
||||
"Key": "",
|
||||
"Audience": "",
|
||||
"Issuer": "",
|
||||
"ValidityInMinutes": 5,
|
||||
"RefreshTokenValidityInDays": 10
|
||||
}
|
||||
}
|
||||
|
10
SharedModels/Requests/ChangeEmailRequest.cs
Normal file
10
SharedModels/Requests/ChangeEmailRequest.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SharedModels.Requests;
|
||||
|
||||
public class ChangeEmailRequest
|
||||
{
|
||||
[Required]
|
||||
[DataType(DataType.EmailAddress)]
|
||||
public string NewEmail { get; set; } = null!;
|
||||
}
|
21
SharedModels/Requests/ChangeInformationRequest.cs
Normal file
21
SharedModels/Requests/ChangeInformationRequest.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Utils;
|
||||
|
||||
namespace SharedModels.Requests;
|
||||
|
||||
public class ChangeInformationRequest
|
||||
{
|
||||
[Required]
|
||||
public string FistName { get; set; } = null!;
|
||||
[Required]
|
||||
public string LastName { get; set; } = null!;
|
||||
[Required]
|
||||
public string Patronymic { get; set; } = null!;
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.Date)]
|
||||
public DateTime BirthDate { get; set; }
|
||||
|
||||
[Required]
|
||||
public Identity.Gender Gender { get; set; }
|
||||
}
|
12
SharedModels/Requests/ChangePasswordRequest.cs
Normal file
12
SharedModels/Requests/ChangePasswordRequest.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SharedModels.Requests;
|
||||
|
||||
public class ChangePasswordRequest
|
||||
{
|
||||
[Required]
|
||||
public string CurrentPassword { get; set; } = null!;
|
||||
|
||||
[Required]
|
||||
public string NewPassword { get; set; } = null!;
|
||||
}
|
10
SharedModels/Requests/ChangePhoneNumberRequest.cs
Normal file
10
SharedModels/Requests/ChangePhoneNumberRequest.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SharedModels.Requests;
|
||||
|
||||
public class ChangePhoneNumberRequest
|
||||
{
|
||||
[Required]
|
||||
[DataType(DataType.PhoneNumber)]
|
||||
public string PhoneNumber { get; set; } = null!;
|
||||
}
|
9
SharedModels/Requests/ConfirmChangeEmailRequest.cs
Normal file
9
SharedModels/Requests/ConfirmChangeEmailRequest.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SharedModels.Requests;
|
||||
|
||||
public class ConfirmChangeEmailRequest : ChangeEmailRequest
|
||||
{
|
||||
[Required]
|
||||
public string Token { get; set; } = null!;
|
||||
}
|
9
SharedModels/Requests/ConfirmChangePhoneNumberRequest.cs
Normal file
9
SharedModels/Requests/ConfirmChangePhoneNumberRequest.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SharedModels.Requests;
|
||||
|
||||
public class ConfirmChangePhoneNumberRequest : ChangePhoneNumberRequest
|
||||
{
|
||||
[Required]
|
||||
public string Token { get; set; } = null!;
|
||||
}
|
13
SharedModels/Requests/ConfirmRegistrationEmailRequest.cs
Normal file
13
SharedModels/Requests/ConfirmRegistrationEmailRequest.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SharedModels.Requests;
|
||||
|
||||
public class ConfirmRegistrationEmailRequest
|
||||
{
|
||||
[Required]
|
||||
[DataType(DataType.EmailAddress)]
|
||||
public string Email { get; set; } = null!;
|
||||
|
||||
[Required]
|
||||
public string Token { get; set; } = null!;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SharedModels.Requests;
|
||||
|
||||
public class ConfirmRegistrationPhoneNumberRequest
|
||||
{
|
||||
[Required]
|
||||
[DataType(DataType.PhoneNumber)]
|
||||
public string PhoneNumber { get; set; } = null!;
|
||||
|
||||
[Required]
|
||||
public string Token { get; set; } = null!;
|
||||
}
|
@ -24,7 +24,4 @@ public class RegistrationRequest
|
||||
[Required(ErrorMessage = "Password is required")]
|
||||
[DataType(DataType.Password)]
|
||||
public string Password { get; set; } = null!;
|
||||
|
||||
[Url]
|
||||
public string EmailConfirmationRedirectUrl { get; set; } = null!;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user