<commit_before>﻿using MediatR;
using NLog;
using SFA.DAS.EmployerUsers.Application.Services.Notification;
using SFA.DAS.EmployerUsers.Application.Services.Password;
using SFA.DAS.EmployerUsers.Application.Validation;
using SFA.DAS.EmployerUsers.Domain;
using SFA.DAS.EmployerUsers.Domain.Auditing;
using SFA.DAS.EmployerUsers.Domain.Auditing.Login;
using SFA.DAS.EmployerUsers.Domain.Data;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace SFA.DAS.EmployerUsers.Application.Commands.PasswordReset
{
    public class PasswordResetCommandHandler : IAsyncRequestHandler<PasswordResetCommand, PasswordResetResponse>
    {
        private readonly ILogger _logger;
        private readonly IAuditService _auditService;
        private readonly IUserRepository _userRepository;
        private readonly IValidator<PasswordResetCommand> _validator;
        private readonly ICommunicationService _communicationService;
        private readonly IPasswordService _passwordService;

        public PasswordResetCommandHandler(
            IUserRepository userRepository,
            IValidator<PasswordResetCommand> validator,
            ICommunicationService communicationService,
            IPasswordService passwordService,
            ILogger logger,
            IAuditService auditService)
        {
            _userRepository = userRepository;
            _validator = validator;
            _communicationService = communicationService;
            _passwordService = passwordService;
            _logger = logger;
            _auditService = auditService;
        }

        public async Task<PasswordResetResponse> Handle(PasswordResetCommand message)
        {
            _logger.Info($"Received PasswordResetCommand for user id '{message.User?.Id}'");

            var user = await _userRepository.GetByEmailAddress(message.Email);
            message.User = user;

            var validationResult = await _validator.ValidateAsync(message);

            if (!validationResult.IsValid())
            {
                throw new InvalidRequestException(validationResult.ValidationDictionary);
            }

            var resetCode = message.User?.SecurityCodes?.OrderByDescending(sc => sc.ExpiryTime)
                                                    .FirstOrDefault(sc => sc.Code.Equals(message.PasswordResetCode, StringComparison.InvariantCultureIgnoreCase)
                                                                       && sc.CodeType == Domain.SecurityCodeType.PasswordResetCode);

            var securedPassword = await _passwordService.GenerateAsync(message.Password);

            message.User.Password = <PASSWORD>;
            message.User.PasswordProfileId = securedPassword.ProfileId;
            message.User.Salt = securedPassword.Salt;
            message.User.IsActive = true;
            message.User.ExpireSecurityCodesOfType(SecurityCodeType.AccessCode);
            message.User.ExpireSecurityCodesOfType(SecurityCodeType.PasswordResetCode);

            await _auditService.WriteAudit(new PasswordResetAuditMessage(message.User));

            await _userRepository.Update(message.User);
            _logger.Info($"Password changed for user id '{message.User.Id}'");

            return new PasswordResetResponse { ResetCode = resetCode };
        }
    }

    public class PasswordResetResponse
    {
        public SecurityCode ResetCode { get; set; }
    }
}<commit_msg>[CON-3842] Update PasswordResetCommandHandler to raise exception on and after 3rd attempt to reset password using code<commit_after>﻿using System.Threading.Tasks;
using MediatR;
using NLog;
using SFA.DAS.EmployerUsers.Application.Exceptions;
using SFA.DAS.EmployerUsers.Application.Extensions;
using SFA.DAS.EmployerUsers.Application.Services.Notification;
using SFA.DAS.EmployerUsers.Application.Services.Password;
using SFA.DAS.EmployerUsers.Application.Validation;
using SFA.DAS.EmployerUsers.Domain;
using SFA.DAS.EmployerUsers.Domain.Auditing;
using SFA.DAS.EmployerUsers.Domain.Auditing.Login;
using SFA.DAS.EmployerUsers.Domain.Data;

namespace SFA.DAS.EmployerUsers.Application.Commands.PasswordReset
{
    public class PasswordResetCommandHandler : IAsyncRequestHandler<PasswordResetCommand, PasswordResetResponse>
    {
        private readonly ILogger _logger;
        private readonly IAuditService _auditService;
        private readonly IUserRepository _userRepository;
        private readonly IValidator<PasswordResetCommand> _validator;
        private readonly ICommunicationService _communicationService;
        private readonly IPasswordService _passwordService;

        public PasswordResetCommandHandler(
            IUserRepository userRepository,
            IValidator<PasswordResetCommand> validator,
            ICommunicationService communicationService,
            IPasswordService passwordService,
            ILogger logger,
            IAuditService auditService)
        {
            _userRepository = userRepository;
            _validator = validator;
            _communicationService = communicationService;
            _passwordService = passwordService;
            _logger = logger;
            _auditService = auditService;
        }

        public async Task<PasswordResetResponse> Handle(PasswordResetCommand message)
        {
            _logger.Info($"Received PasswordResetCommand for user id '{message.User?.Id}'");

            var user = await _userRepository.GetByEmailAddress(message.Email);
            message.User = user;

            var validationResult = await _validator.ValidateAsync(message);

            if (!validationResult.IsValid())
            {
                await ProcessFailedAttempt(user);
                throw new InvalidRequestException(validationResult.ValidationDictionary);
            }

            var resetCode = message.User?.SecurityCodes?.MatchSecurityCode(message.PasswordResetCode);

            var securedPassword = await _passwordService.GenerateAsync(message.Password);

            message.User.Password = <PASSWORD>;
            message.User.PasswordProfileId = securedPassword.ProfileId;
            message.User.Salt = securedPassword.Salt;
            message.User.IsActive = true;
            message.User.ExpireSecurityCodesOfType(SecurityCodeType.AccessCode);
            message.User.ExpireSecurityCodesOfType(SecurityCodeType.PasswordResetCode);

            await _auditService.WriteAudit(new PasswordResetAuditMessage(message.User));

            await _userRepository.Update(message.User);
            _logger.Info($"Password changed for user id '{message.User.Id}'");

            return new PasswordResetResponse { ResetCode = resetCode };
        }

        private async Task ProcessFailedAttempt(User user)
        {
            var latestSecurityCode = user?.SecurityCodes?.LatestValidSecurityCode();
            
            if (latestSecurityCode != null)
            {
                latestSecurityCode.FailedAttempts += 1;
                await _userRepository.Update(user);

                if (latestSecurityCode.FailedAttempts>= 3)
                {
                    throw new PasswordResetMaxAttemptsException();
                }
            }
        }
    }

    public class PasswordResetResponse
    {
        public SecurityCode ResetCode { get; set; }
    }
}
{'fraction_non_alphanumeric': 0.07012278308321965, 'fraction_numerical': 0.0009549795361527968, 'mean_word_length': 2.711898734177215, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7325681', 'n_tokens_mistral': 1793, 'n_tokens_neox': 1734, 'n_words': 340}