<repo_name>DziugasMazulis/ToDO-List-REST-API<file_sep>/TO-DO_List/Models/ToDoTaskRequest.cs
﻿using System.ComponentModel.DataAnnotations;

namespace TO_DO_List.Models.Dto
{
    public class ToDoTaskRequest
    {
        [Required]
        public string Title { get; set; }
        public bool IsCompleted { get; set; }
    }
}
<file_sep>/TO-DO_List/Settings/SeedDataSettings.cs
﻿using System.Collections.Generic;
using TO_DO_List.Models.Dto;

namespace TO_DO_List.Settings
{
    public class SeedDataSettings
    {
        public List<UserSettings> Users { get; set; }
        public List<ToDoTaskSettings> ToDoTasks { get; set; }
    }
}
<file_sep>/TO-DO_List/Security/AuthOperationFilter.cs
﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Collections.Generic;
using System.Linq;
using TO_DO_List.Data;

namespace TO_DO_List.Security
{
    public class AuthOperationFilter : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {

            var isAuthorized = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any() ||
                              context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();

            if (!isAuthorized) return;

            operation.Responses.TryAdd(Constants.Int401, new OpenApiResponse { Description = Constants.Unauthorized });
            operation.Responses.TryAdd(Constants.Int403, new OpenApiResponse { Description = Constants.Forbidden });

            var jwtbearerScheme = new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = Constants.Bearer }
            };

            operation.Security = new List<OpenApiSecurityRequirement>
               {
                   new OpenApiSecurityRequirement
                   {
                       [ jwtbearerScheme ] = new string [] { }
                   }
               };
        }
    }
}
<file_sep>/TO-DO_List/Program.cs
using System;
using System.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using TO_DO_List.Contracts.Services;

namespace TO_DO_List
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();

            SeedDatabase(host);

            host.Run();
        }

        private static void SeedDatabase(IHost host)
        {
            using (var scope = host.Services.CreateScope())
            {
                var serviceProvider = scope.ServiceProvider;
                try
                {
                    //not the most efficient way to clear data but hey *shrugEmoji*
                    var databaseService = serviceProvider.GetRequiredService<IDatabaseService>();
                    databaseService.EnsureDeleted();
                    databaseService.EnsureCreated();
                    databaseService.SeedRoles();
                    databaseService.SeedUsers();
                    databaseService.SeedTasks();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}
<file_sep>/TO-DO_List/Data/ToDoTaskProfile.cs
﻿using AutoMapper;
using TO_DO_List.Models;
using TO_DO_List.Models.Dto;
using TO_DO_List.ViewModels;

namespace TO_DO_List.Data
{
    public class ToDoTaskProfile : Profile
    {
        public ToDoTaskProfile()
        {
            CreateMap<ToDoTask, ToDoTaskResponse>()
                .ForMember(dest =>
                dest.Username,
                opt => opt.MapFrom(src => src.User.UserName));

            CreateMap<ToDoTaskRequest, ToDoTask>()
                .ForMember(dest =>
                dest.User,
                opt => opt.Ignore());

            CreateMap<ToDoTask, ToDoTask>()
                .ForMember(dest =>
                dest.User,
                opt => opt.Ignore());
        }
    }
}
<file_sep>/TO-DO_List/Repositories/ToDoTaskRepository.cs
﻿using AutoMapper;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TO_DO_List.Contracts.Repositories;
using TO_DO_List.Data;
using TO_DO_List.Models;

namespace TO_DO_List.Repositories
{
    public class ToDoTaskRepository : IToDoTaskRepository
    {
        private readonly ApplicationContext _applicationContext;
        private readonly IMapper _mapper;

        public ToDoTaskRepository(ApplicationContext applicationContext,
            IMapper mapper)
        {
            _applicationContext = applicationContext;
            _mapper = mapper;
        }

        public async Task<ToDoTask> AddToDoTaskAsync(ToDoTask toDoTask)
        {
            var result = await _applicationContext.ToDoTasks
                .AddAsync(toDoTask);
            await _applicationContext.SaveChangesAsync();

            return result.Entity;
        }

        public async Task<ToDoTask> DeleteToDoTaskAsync(int toDoTaskId)
        {
            var result = await _applicationContext.ToDoTasks
                .FirstOrDefaultAsync(t => t.ID == toDoTaskId);

            if (result == null)
                return result;

            _applicationContext.ToDoTasks.Remove(result);
            await _applicationContext.SaveChangesAsync();

            return result;
        }

        public Task<ToDoTask> GetToDoTaskAsync(int toDoTaskId)
        {
            return _applicationContext.ToDoTasks
                .FirstOrDefaultAsync(t => t.ID == toDoTaskId);
        }


        public Task<List<ToDoTask>> GetToDoTaskByUserIdAsync(string id)
        {
            return _applicationContext.ToDoTasks
                .Where(x => x.User.Id.Equals(id))
                .ToListAsync();
        }

        public Task<List<ToDoTask>> GetToDoTasksAsync()
        {
            return _applicationContext.ToDoTasks
                .ToListAsync();
        }

        public async Task<ToDoTask> UpdateToDoTaskAsync(ToDoTask toDoTask)
        {
            var result = await _applicationContext.ToDoTasks
                .FirstOrDefaultAsync(t => t.ID == toDoTask.ID);

            if (result == null)
                return result;

            _mapper.Map(toDoTask, result);

            await _applicationContext.SaveChangesAsync();

            return result;
        }
    }
}
<file_sep>/TO-DO_List/Contracts/Services/IToDoTaskService.cs
﻿using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using TO_DO_List.Models.Dto;
using TO_DO_List.ViewModels;

namespace TO_DO_List.Contracts.Services
{
    public interface IToDoTaskService
    {
        Task<IEnumerable<ToDoTaskResponse>> GetToDoTasks(ClaimsPrincipal user);
        Task<ToDoTaskResponse> AddToDoTask(ClaimsPrincipal user, ToDoTaskRequest toDoTaskDto);
        Task<ToDoTaskResponse> UpdateToDoTask(ClaimsPrincipal user, int id, ToDoTaskRequest toDoTask);
        Task<ToDoTaskResponse> DeleteToDoTask(ClaimsPrincipal user, int id);
    }
}
<file_sep>/TO-DO_List/Services/AccountService.cs
﻿using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;
using TO_DO_List.Contracts.Services;
using TO_DO_List.Models;

namespace TO_DO_List.Services
{
    public class AccountService : IAccountService
    {
        private readonly UserManager<User> _userManager;
        private readonly SignInManager<User> _signInManager;
        private readonly IJwtService _jwtService;

        public AccountService(UserManager<User> userManager,
            SignInManager<User> signInManager,
            IJwtService jwtService)
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _jwtService = jwtService;
        }

        public async Task<string> Login(LoginRequest loginDto)
        {
            var user = await _userManager.FindByEmailAsync(loginDto.Email);

            if (user == null)
                return null;

            var passwordCheck = await _signInManager.CheckPasswordSignInAsync(user, loginDto.Password, false);

            if (passwordCheck == null &&
                !passwordCheck.Succeeded)
                return null;

            var roles = await _userManager.GetRolesAsync(user);

            if (roles == null)
                return null;
             
            var token = _jwtService.GenerateJwt(user, roles);

            if (token == null ||
                token.Length <= 0)
                return null;

            return token;
        }


        public async Task<ForgotPasswordResponse> ForgotPassword(ForgotPasswordRequest forgotPassword)
        {
            var user = await _userManager.FindByEmailAsync(forgotPassword.Email);

            if (user == null)
                return null;
             
            var token = await _userManager.GeneratePasswordResetTokenAsync(user);

            if (token == null)
                return null;
                
            var fotgotPasswordRespone = new ForgotPasswordResponse();
            fotgotPasswordRespone.Email = user.Email;
            fotgotPasswordRespone.Token = token;

            return fotgotPasswordRespone;
        }

        public async Task<IdentityResult> ResetPassword(string token, string email, ResetPasswordRequest resetPasswordRequest)
        {
            var user = await _userManager.FindByEmailAsync(email);

            if (user == null)
                return null;
             
            var resetPassResult = await _userManager.ResetPasswordAsync(user, token, resetPasswordRequest.Password);
                return resetPassResult;
        }
    }
}
<file_sep>/TO-DO_List/Entities/User.cs
﻿using Microsoft.AspNetCore.Identity;
using System.Collections.Generic;

namespace TO_DO_List.Models
{
    public class User : IdentityUser
    {
        public virtual IEnumerable<ToDoTask> ToDoTasks { get; set; }
    }
}
<file_sep>/TO-DO_List/Extensions/SwaggerExtensions.cs
﻿using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using TO_DO_List.Security;

namespace TO_DO_List.Extensions
{
    public static class SwaggerExtensions
    {
        public static IServiceCollection AddSwag(this IServiceCollection services)
        {
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "ToDo-List", Version = "v1" });
                c.AddSecurityDefinition("bearer", new OpenApiSecurityScheme
                {
                    Name = "Authorization",
                    Type = SecuritySchemeType.ApiKey,
                    Scheme = "bearer",
                    BearerFormat = "JWT",
                    In = ParameterLocation.Header,
                    Description = "JWT Authorization header using the Bearer scheme.",
                });

                c.OperationFilter<AuthOperationFilter>();
            });

            return services;
        }

        public static IApplicationBuilder UseSwag(this IApplicationBuilder app)
        {
            app.UseSwagger();

            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "TestService");
                c.OAuthClientId("cleint-id");
                c.OAuthClientSecret("client-secret");
                c.OAuthRealm("client-realm");
                c.OAuthAppName("OAuth-app");
                c.OAuthUseBasicAuthenticationWithAccessCodeGrant();
            });

            return app;
        }
    }
}
<file_sep>/TO-DO_List/Controllers/ToDoTaskController.cs
﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using TO_DO_List.Contracts.Services;
using TO_DO_List.Data;
using TO_DO_List.Models.Dto;
using TO_DO_List.ViewModels;

namespace TO_DO_List.Controllers
{
    [Route("[controller]")]
    [ApiController]
    [Authorize]
    public class ToDoTaskController : ControllerBase
    {
        private readonly IToDoTaskService _toDoTaskService;

        public ToDoTaskController(IToDoTaskService toDoTaskService)
        {
            _toDoTaskService = toDoTaskService;
        }

        /// <summary>
        /// Gets all auhtorized existing ToDo tasks.
        /// </summary>
        /// <returns>To admin - all ToDo tasks, to user - all user's ToDo tasks</returns>
        [HttpGet("GetTasks")]
        public async Task<ActionResult> GetToDoTasks()
        {
            try
            {
                var result = await _toDoTaskService.GetToDoTasks(HttpContext.User);

                if (result == null)
                    return NotFound();

                return Ok(result);
            }
            catch (Exception)
            {
                return StatusCode(StatusCodes.Status500InternalServerError,
                    Constants.ErrorRetrieveDatabase);
            }
        }

        /// <summary>
        /// Create a ToDo task.
        /// </summary>
        /// <param name="toDoTask">ToDo task request model</param>
        /// <returns>A newly created ToDo task</returns>
        [Authorize(Roles="user")]
        [HttpPost]
        public async Task<ActionResult<ToDoTaskResponse>> CreateToDoTask(ToDoTaskRequest toDoTask)
        {
            if (!ModelState.IsValid)
                return BadRequest();

            try
            {
                var result = await _toDoTaskService.AddToDoTask(HttpContext.User, toDoTask);

                return result;
            }
            catch (Exception)
            {
                return StatusCode(StatusCodes.Status500InternalServerError,
                    Constants.ErrorRetrieveDatabase);
            }
        }

        /// <summary>
        /// Updates chosed ToDo task.
        /// </summary>
        /// <param name="id">Chosen ToDo task's ID</param>
        /// <param name="toDoTask">ToDo task's request model to be changed into containing of ToDo task's title and completion status</param>
        /// <returns>Changed ToDo task</returns>
        [Authorize(Roles="user")]
        [HttpPut("{id:int}")]
        public async Task<ActionResult<ToDoTaskResponse>> UpdateToDoTask(int id, ToDoTaskRequest toDoTask)
        {
            if (!ModelState.IsValid)
                return BadRequest();

            try
            {
                var result = await _toDoTaskService.UpdateToDoTask(HttpContext.User, id, toDoTask);

                if (result == null)
                    return NotFound(string.Format(Constants.TaskNotFound, id));

                return Ok(result);
            }
            catch (Exception)
            {
                return StatusCode(StatusCodes.Status500InternalServerError,
                    Constants.ErrorUpdateDatabase);
            }
        }

        /// <summary>
        /// Deletes a chosen ToDo task.
        /// </summary>
        /// <param name="id">ID of a chosen ToDo task to be deleted</param>
        /// <returns>A deleted ToDo task's response model</returns>
        [HttpDelete("{id:int}")]
        public async Task<ActionResult<ToDoTaskResponse>> DeleteToDoTask(int id)
        {
            try
            {
                var result = await _toDoTaskService.DeleteToDoTask(HttpContext.User, id);

                if (result == null)
                    return NotFound(string.Format(Constants.TaskNotFound, id));

                return Ok(result);
            }
            catch (Exception)
            {
                return StatusCode(StatusCodes.Status500InternalServerError,
                    Constants.ErrorDeleteDatabase);
            }
        }
    }
}
<file_sep>/TO-DO_List/Models/ToDoTaskResponse.cs
﻿namespace TO_DO_List.ViewModels
{
    public class ToDoTaskResponse
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public bool IsCompleted { get; set; }
        public string Username { get; set; }
    }
}
<file_sep>/TO-DO_List/Contracts/Services/IJwtService.cs
﻿using System.Collections.Generic;
using TO_DO_List.Models;

namespace TO_DO_List.Contracts.Services
{
    public interface IJwtService
    {
        string GenerateJwt(User user, IList<string> roles);
    }
}
<file_sep>/TO-DO_List/Controllers/AccountController.cs
﻿using System;
using System.Threading.Tasks;
using EmailService;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using TO_DO_List.Contracts.Services;
using TO_DO_List.Data;
using TO_DO_List.Models;

namespace TO_DO_List.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class AccountController : ControllerBase
    {
        private readonly SignInManager<User> _signInManager;
        private readonly UserManager<User> _userManager;
        private readonly IJwtService _jwtService;
        private readonly IEmailSender _emailSender;
        private readonly IAccountService _accountService;

        public AccountController(SignInManager<User> signInManager,
            UserManager<User> userManager,
            IJwtService jwtService,
            IEmailSender emailSender,
            IAccountService accountService)
        {
            _signInManager = signInManager;
            _userManager = userManager;
            _jwtService = jwtService;
            _emailSender = emailSender;
            _accountService = accountService;
        }

        /// <summary>
        /// Authenticates user.
        /// </summary>
        /// <param name="loginRequest">A login request model containing of user email and password</param>
        /// <returns>On success JWT token</returns>
        [HttpPost("Login")]
        public async Task<IActionResult> Login([FromBody] LoginRequest loginRequest)
        {
            if (!ModelState.IsValid)
                return BadRequest();
            try
            {
                var token = await _accountService.Login(loginRequest);

                if (token == null)
                    return NotFound();

                return Ok(token);
            }
            catch (Exception)
            {
                return StatusCode(StatusCodes.Status500InternalServerError,
                    Constants.ErrorRetrieveDatabase);
            }
        }

        /// <summary>
        /// Account recovery action sends password reset link to user's email
        /// </summary>
        /// <param name="forgotPasswordRequest">Forgot password request model containing user's email </param>
        /// <returns>On success sends password recovery link to user's mail</returns>
        [Authorize(Roles="user")]
        [HttpPost("ForgotPassword")]
        public async Task<IActionResult> ForgotPassword([FromBody] ForgotPasswordRequest forgotPasswordRequest)
        {
            if (!ModelState.IsValid)
                return BadRequest();

            try
            {
                var forgotPasswordResponse = await _accountService.ForgotPassword(forgotPasswordRequest);

                if (forgotPasswordResponse == null)
                    return NotFound(string.Format(Constants.UserNotFound, forgotPasswordRequest.Email));

                var callback = Url.Action(nameof(ResetPassword), Constants.Account, new { token = forgotPasswordResponse.Token, email = forgotPasswordResponse.Email }, Request.Scheme);
                var message = new Message(new string[] { forgotPasswordRequest.Email }, Constants.ResetPasswordToken, callback, null);
                await _emailSender.SendEmailAsync(message);

                return Ok(string.Format(Constants.PasswordResetLinkSent, forgotPasswordRequest.Email));
            }
            catch (Exception)
            {
                return StatusCode(StatusCodes.Status500InternalServerError,
                    Constants.ErrorPasswordResetEmail);
            }
            
        }

        /// <summary>
        /// Changes user's password
        /// </summary>
        /// <param name="resetPasswordRequest">Reset password request model containing of new password and it's confirmation</param>
        /// <param name="token">User's password reset token</param>
        /// <param name="email">User's email</param>
        /// <returns>Operation's success status</returns>
        [Authorize(Roles="user")]
        [HttpPost("ResetPassword")]
        public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordRequest resetPasswordRequest, string token, string email)
        {
            if (!ModelState.IsValid ||
                token == null ||
                 email == null)
                return BadRequest(new { resetPasswordRequest, token, email });

            try
            {
                var result = await _accountService.ResetPassword(token, email, resetPasswordRequest);

                if (result == null)
                    return NotFound(string.Format(Constants.UserNotFound, email));

                if(result.Succeeded)
                    return Ok(Constants.PasswordReset);

                else
                {
                    foreach (var error in result.Errors)
                    {
                        ModelState.TryAddModelError(error.Code, error.Description);
                    }
                    return StatusCode(StatusCodes.Status422UnprocessableEntity,
                        Constants.InvalidToken);
                }

            }
            catch (Exception)
            {
                return StatusCode(StatusCodes.Status500InternalServerError,
                    Constants.ErrorResetPassword);
            }
        }
    }
}
<file_sep>/TO-DO_List/Services/ToDoTaskService.cs
﻿using AutoMapper;
using Microsoft.AspNetCore.Identity;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using TO_DO_List.Contracts.Repositories;
using TO_DO_List.Contracts.Services;
using TO_DO_List.Data;
using TO_DO_List.Models;
using TO_DO_List.Models.Dto;
using TO_DO_List.ViewModels;

namespace TO_DO_List.Services
{
    public class ToDoTaskService : IToDoTaskService
    {
        private readonly UserManager<User> _userManager;
        private readonly IToDoTaskRepository _toDoTaskRepository;
        private readonly IMapper _mapper;

        public ToDoTaskService(UserManager<User> userManager,
            IToDoTaskRepository toDoTaskRepository,
            IMapper mapper)
        {
            _userManager = userManager;
            _toDoTaskRepository = toDoTaskRepository;
            _mapper = mapper;
        }


        public async Task<IEnumerable<ToDoTaskResponse>> GetToDoTasks(ClaimsPrincipal user)
        {
            var currUser = await _userManager.GetUserAsync(user);

            if (currUser == null)
                return null;

            var currUserRoles = await _userManager.GetRolesAsync(currUser);

            if (currUserRoles == null)
                return null;

            List<ToDoTask> result = null;

            if (currUserRoles.Contains(Constants.Admin))
            {
                result = await _toDoTaskRepository.GetToDoTasksAsync();
            }
            else if (currUserRoles.Contains(Constants.User))
            {
                result = await _toDoTaskRepository.GetToDoTaskByUserIdAsync(currUser.Id);
            }

            if (result == null)
                return null;
         
            var toDoTaskResponse = _mapper.Map<List<ToDoTask>, List<ToDoTaskResponse>>(result);

            return toDoTaskResponse;
        }

        public async Task<ToDoTaskResponse> AddToDoTask(ClaimsPrincipal user, ToDoTaskRequest toDoTaskRequest)
        {
            var currUser = await _userManager.GetUserAsync(user);

            if (currUser == null)
                return null;

            var toDoTask = _mapper.Map<ToDoTask>(toDoTaskRequest, opt =>
                opt.AfterMap((src, dest) => dest.User = currUser));
            toDoTask = await _toDoTaskRepository.AddToDoTaskAsync(toDoTask);

            if (toDoTask == null)
                return null;

            var toDoTaskResponse = _mapper.Map<ToDoTaskResponse>(toDoTask);

            return toDoTaskResponse;
        }

        public async Task<ToDoTaskResponse> UpdateToDoTask(ClaimsPrincipal user, int id, ToDoTaskRequest toDoTaskRequest)
        {
            var result = await _toDoTaskRepository.GetToDoTaskAsync(id);

            if (result == null)
                return null;
         
            var currUser = await _userManager.GetUserAsync(user);

            if (currUser == null ||
                result.User == null ||
                result.User.UserName != currUser.UserName)
                return null;

            var toDoTask = _mapper.Map<ToDoTask>(toDoTaskRequest, opt =>
                opt.AfterMap((src, dest) => dest.ID = id));

            result = await _toDoTaskRepository.UpdateToDoTaskAsync(toDoTask);

            if (result == null)
                return null;
         
            var toDoTaskResponse = _mapper.Map<ToDoTaskResponse>(result);

            return toDoTaskResponse;
        }
        public async Task<ToDoTaskResponse> DeleteToDoTask(ClaimsPrincipal user, int id)
        {
            var toDoTask = await _toDoTaskRepository.GetToDoTaskAsync(id);

            if (toDoTask == null)
                return null;

            var currUser = await _userManager.GetUserAsync(user);

            if (currUser == null)
                return null;

            var currUserRoles = await _userManager.GetRolesAsync(currUser);

            if (currUserRoles == null)
                return null;

            if (!(toDoTask.User.UserName == currUser.UserName && currUserRoles.Contains(Constants.User)) &&
                !currUserRoles.Contains(Constants.Admin))
                return null;

            var toDoTaskResult = await _toDoTaskRepository.DeleteToDoTaskAsync(id);

            if (toDoTaskResult == null)
                return null;

            var toDoTaskResponse = _mapper.Map<ToDoTaskResponse>(toDoTask);

            return toDoTaskResponse;
        }
    }
}
<file_sep>/TO-DO_List/Contracts/Repositories/IToDoTaskRepository.cs
﻿using System.Collections.Generic;
using System.Threading.Tasks;
using TO_DO_List.Models;

namespace TO_DO_List.Contracts.Repositories
{
    public interface IToDoTaskRepository
    {
        Task<ToDoTask> AddToDoTaskAsync(ToDoTask toDoTask);
        Task<ToDoTask> DeleteToDoTaskAsync(int toDoTaskId);
        Task<ToDoTask> GetToDoTaskAsync(int toDoTaskId);
        Task<List<ToDoTask>> GetToDoTaskByUserIdAsync(string id);
        Task<List<ToDoTask>> GetToDoTasksAsync();
        Task<ToDoTask> UpdateToDoTaskAsync(ToDoTask toDoTask);
    }
}
<file_sep>/TO-DO_List/Models/ToDoTaskSettings.cs
﻿namespace TO_DO_List.Models.Dto
{
    public class ToDoTaskSettings
    {
        public string Title { get; set; }
        public bool IsCompleted { get; set; }
        public string User { get; set; }
    }
}
<file_sep>/TO-DO_List/Models/ResetPasswordRequest.cs
﻿using System.ComponentModel.DataAnnotations;
using TO_DO_List.Data;

namespace TO_DO_List.Models
{
    public class ResetPasswordRequest
    {
        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }
        [DataType(DataType.Password)]
        [Compare(Constants.Password, ErrorMessage = Constants.NotMach)]
        public string ConfirmPassword { get; set; }
    }
}
<file_sep>/TO-DO_List/Services/DatabaseService.cs
﻿using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System.Security.Claims;
using TO_DO_List.Contracts.Services;
using TO_DO_List.Data;
using TO_DO_List.Models;
using TO_DO_List.Models.Dto;
using TO_DO_List.Settings;

namespace TO_DO_List.Services
{
    public class DatabaseService : IDatabaseService
    {
        private readonly RoleManager<IdentityRole> _roleManager;
        private readonly UserManager<User> _userManager;
        private readonly SeedDataSettings _seedDataSettings;
        private readonly ApplicationContext _applicationContext;

        public DatabaseService(RoleManager<IdentityRole> roleManager,
            UserManager<User> userManager,
            IOptions<SeedDataSettings> seedDataSettings,
            ApplicationContext applicationContext)
        {
            _roleManager = roleManager;
            _userManager = userManager;
            _seedDataSettings = seedDataSettings.Value;
            _applicationContext = applicationContext;
        }

        /// <summary>
        /// Seed roles located in program configuration.
        /// </summary>
        public void SeedRoles()
        {
            foreach(UserSettings userDto in _seedDataSettings.Users)
            {
                var result = _roleManager.RoleExistsAsync(userDto.Role).Result;

                if (!result)
                {
                    var role = new IdentityRole();
                    role.Name = userDto.Role;

                    var roleResult = _roleManager.
                        CreateAsync(role).Result;

                    if (!roleResult.Succeeded)
                        throw new System.InvalidOperationException();
                }
            }
        }

        /// <summary>
        /// Seed users located in program configuration.
        /// </summary>
        public void SeedUsers()
        {
            foreach(UserSettings userDto in _seedDataSettings.Users)
            {
                var user = _userManager.FindByEmailAsync(userDto.Username).Result;
                if (user == null)
                {
                    user = new User();
                    user.UserName = userDto.Username;
                    user.Email = userDto.Username;

                    var identityUser = _userManager.CreateAsync(user, userDto.Password).Result;

                    if (identityUser.Succeeded)
                    {
                        _userManager.AddToRoleAsync(user, userDto.Role).Wait();
                        _userManager.AddClaimAsync(user, new Claim(ClaimTypes.Role, userDto.Role)).Wait();
                    }
                    else
                        throw new System.InvalidOperationException();
                }
            }
        }

        /// <summary>
        /// Seed tasks located in program configuration.
        /// </summary>
        public void SeedTasks()
        {

            foreach (ToDoTaskSettings toDoTaskDto in _seedDataSettings.ToDoTasks)
            {
                var user = _userManager.FindByEmailAsync(toDoTaskDto.User).Result;
               
                if (user != null)
                {
                    var userRoles = _userManager.GetRolesAsync(user);

                    if(userRoles != null &&
                        !userRoles.Result.Contains(Constants.Admin))
                    {
                        ToDoTask toDoTask = new ToDoTask
                        {
                            Title = toDoTaskDto.Title,
                            IsCompleted = toDoTaskDto.IsCompleted,
                            User = user
                        };

                        _applicationContext.ToDoTasks.Add(toDoTask);
                    }
                }
            }

            _applicationContext.SaveChanges();
        }

        /// <summary>
        /// Ensure that the database for the content exists. If not then the database is created.
        /// </summary>
        /// <returns>True if database is created, false if it already existed.</returns>
        public bool EnsureCreated() => _applicationContext.Database.EnsureCreated();

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool EnsureDeleted() => _applicationContext.Database.EnsureDeleted();
    }
}
<file_sep>/TO-DO_List/Contracts/Services/IDatabaseService.cs
﻿namespace TO_DO_List.Contracts.Services
{
    public interface IDatabaseService
    {
        bool EnsureCreated();
        bool EnsureDeleted();
        void SeedRoles();
        void SeedUsers();
        void SeedTasks();
    }
}
<file_sep>/TO-DO_List/Startup.cs
using AutoMapper;
using EmailService;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using TO_DO_List.Contracts.Repositories;
using TO_DO_List.Contracts.Services;
using TO_DO_List.Data;
using TO_DO_List.Extensions;
using TO_DO_List.Models;
using TO_DO_List.Repositories;
using TO_DO_List.Services;
using TO_DO_List.Settings;

namespace TO_DO_List
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAutoMapper(typeof(Startup));

            var emailConfig = Configuration
                .GetSection(Constants.EmailConfiguration)
                .Get<EmailConfiguration>();

            services.AddDbContextPool<ApplicationContext>(
                options => options.UseLazyLoadingProxies()
                .UseMySql(Configuration.GetConnectionString(Constants.DefaultConnection)));

            services.AddIdentity<User, IdentityRole>(options =>
            {
                options.Password.RequiredLength = 12;
                options.Password.RequireDigit = false;
                options.Password.RequiredUniqueChars = 1;
                options.Password.RequireLowercase = false;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = false;
            })
            .AddEntityFrameworkStores<ApplicationContext>()
            .AddDefaultTokenProviders()
            .AddRoles<IdentityRole>()
            .AddRoleManager<RoleManager<IdentityRole>>();

            services.Configure<DataProtectionTokenProviderOptions>(opt =>
                opt.TokenLifespan = TimeSpan.FromHours(1));

            services.Configure<JwtSettings>(Configuration.GetSection(Constants.Jwt));
            var jwtSettings = Configuration.GetSection(Constants.Jwt).Get<JwtSettings>();
            services.Configure<SeedDataSettings>(options => Configuration.GetSection(Constants.SeedData).Bind(options));

            services.AddAuth(jwtSettings);

            services.AddSwag();

            services.AddControllers();

            services.AddScoped<IJwtService, JwtService>();
            services.AddScoped<IDatabaseService, DatabaseService>();
            services.AddScoped<IToDoTaskRepository, ToDoTaskRepository>();
            services.AddScoped<IToDoTaskService, ToDoTaskService>();
            services.AddScoped<IAccountService, AccountService>();
            services.AddScoped<IEmailSender, EmailSender>();
            services.AddSingleton(emailConfig);
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseSwag();

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuth();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}
<file_sep>/TO-DO_List/Data/ApplicationContext.cs
﻿using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using TO_DO_List.Models;

namespace TO_DO_List.Data
{
    public class ApplicationContext : IdentityDbContext
    {
        public DbSet<ToDoTask> ToDoTasks { get; set; }

        public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options)
        { }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<User>()
                .HasMany(u => u.ToDoTasks)
                .WithOne(t => t.User)
                .OnDelete(DeleteBehavior.Cascade)
                .IsRequired();
        }
    }
}
<file_sep>/TO-DO_List/Contracts/Services/IAccountService.cs
﻿using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;
using TO_DO_List.Models;

namespace TO_DO_List.Contracts.Services
{
    public interface IAccountService
    {
        Task<ForgotPasswordResponse> ForgotPassword(ForgotPasswordRequest forgotPassword);
        Task<IdentityResult> ResetPassword(string token, string email, ResetPasswordRequest resetPasswordRequest);
        Task<string> Login(LoginRequest loginDto);
    }
}
<file_sep>/TO-DO_List/Data/Constants.cs
﻿namespace TO_DO_List.Data
{
    public static class Constants
    {
        public const string Admin = "admin";
        public const string User = "user";
        public const string Account = "Account";
        public const string Password = "<PASSWORD>";


        public const string Int401 = "401";
        public const string Int403 = "403";
        public const string Unauthorized = "Unauthorized";
        public const string Forbidden = "Forbidden";
        public const string Bearer = "bearer";


        public const string Jwt = "Jwt";
        public const string SeedData = "SeedData";
        public const string DefaultConnection = "DefaultConnection";
        public const string EmailConfiguration = "EmailConfiguration";


        public const string ErrorRetrieveDatabase = "Error retrieving data from the database";
        public const string ErrorUpdateDatabase = "Error updating data in database";
        public const string ErrorDeleteDatabase = "Error deleting data in database";
        public const string ErrorPasswordResetEmail = "Error sending password reset email";
        public const string ErrorResetPassword = "<PASSWORD>";
        public const string InvalidToken = "Invalid token";
        public const string ResetPasswordToken = "<PASSWORD>";
        public const string PasswordReset = "<PASSWORD>";
        public const string NotMach = "The password and confirmation password do not match";
        public const string UserNotFound = "User with email {0} not found";
        public const string PasswordResetLinkSent = "Password reset link to email {0} has been sent successfully";
        public const string TaskNotFound = "Task with id {0} not found";
    }
}
