feat: add account management functionality

This commit is contained in:
cuqmbr 2023-06-03 21:26:39 +03:00
parent 5fecec2d3e
commit 287dccd440
25 changed files with 475 additions and 156 deletions

1
.gitignore vendored
View File

@ -9,6 +9,7 @@
*.user
*.userosscache
*.sln.docstates
*.Development.json
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

View File

@ -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();

View 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();
}
}

View File

@ -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")]

View File

@ -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);

View File

@ -107,6 +107,7 @@ services.AddAutoMapper(typeof(MapperInitializer));
services.AddScoped<IEmailSenderService, EmailSenderService>();
services.AddScoped<IAuthenticationService, AuthenticationService>();
services.AddScoped<IAccountManagementService, AccountManagementService>();
services.AddScoped<ISessionUserService, SessionUserService>();

View 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!);
}
}

View File

@ -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();

View File

@ -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");
}

View 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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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!);
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View 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!;
}

View 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; }
}

View 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!;
}

View 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!;
}

View File

@ -0,0 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace SharedModels.Requests;
public class ConfirmChangeEmailRequest : ChangeEmailRequest
{
[Required]
public string Token { get; set; } = null!;
}

View File

@ -0,0 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace SharedModels.Requests;
public class ConfirmChangePhoneNumberRequest : ChangePhoneNumberRequest
{
[Required]
public string Token { get; set; } = null!;
}

View 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!;
}

View File

@ -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!;
}

View File

@ -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!;
}