package ru.soune.nocopy.service.register; import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.soune.nocopy.dto.*; import ru.soune.nocopy.dto.register.AccountType; import ru.soune.nocopy.dto.register.LoginAnswer; import ru.soune.nocopy.dto.register.LoginRequest; import ru.soune.nocopy.dto.register.RegRequest; import ru.soune.nocopy.entity.company.Company; import ru.soune.nocopy.entity.tarif.TariffInfo; import ru.soune.nocopy.entity.tarif.TariffType; import ru.soune.nocopy.entity.user.AuthToken; import ru.soune.nocopy.entity.user.Permission; import ru.soune.nocopy.entity.user.User; import ru.soune.nocopy.entity.user.UserNotActive; import ru.soune.nocopy.exception.CompanyAlreadyExist; import ru.soune.nocopy.exception.NotFoundAuthToken; import ru.soune.nocopy.exception.NotValidFieldException; import ru.soune.nocopy.repository.*; import ru.soune.nocopy.service.file.CheckCounterService; import ru.soune.nocopy.service.tariff.TariffInfoService; import java.security.SecureRandom; import java.time.LocalDateTime; import java.util.*; @Service @RequiredArgsConstructor public class AuthService { private final UserRepository userRepository; private final AuthTokenRepository authTokenRepository; private final PasswordEncoder passwordEncoder; private final CompanyRepository companyRepository; private final CheckCounterService checkCounterService; private final TariffInfoRepository tariffInfoRepository; private final TariffInfoService tariffInfoService; private final UserNotActiveRepository userNotActiveRepository; private final SecureRandom secureRandom = new SecureRandom(); @Transactional public AuthToken register(RegRequest registerRequest, boolean verified) { User user = new User(); user.setFullName(registerRequest.getFullName()); user.setEmail(registerRequest.getEmail()); user.setPassword(passwordEncoder.encode(registerRequest.getPassword())); user.setActive(!verified); user.setEmailVerified(!verified); AccountType accountType = AccountType.valueOf(registerRequest.getAccountType().toUpperCase()); if (accountType == AccountType.B2B && registerRequest.getCompanyName() != null) { try { createCompanyAccount(registerRequest, user); } catch (CompanyAlreadyExist exist) { throw new CompanyAlreadyExist("Company already exist"); } } else if (accountType == AccountType.B2C && registerRequest.getCompanyName() == null) { createIndividualAccount(user); } else { createIndividualCompanyAccount(registerRequest, user); } if (registerRequest.getPhone() != null) { user.setPhone(registerRequest.getPhone()); } User savedUser = userRepository.save(user); AuthToken authToken = genereateAuthToken(savedUser); checkCounterService.initProtectCheckLimit(user, 0); return authTokenRepository.save(authToken); } @Transactional public Long useUserAuthToken(String token) { if (token == null || token.isBlank()) { throw new NotFoundAuthToken("Token is required"); } if (token.startsWith("Bearer ")) { token = token.replace("Bearer ", ""); } AuthToken authToken = authTokenRepository.findByToken(token) .orElseThrow(() -> new NotFoundAuthToken("Token not found")); authToken.setLastUsedAt(LocalDateTime.now()); authToken.setExpiresAt(LocalDateTime.now().plusHours(1)); authTokenRepository.save(authToken); return authToken.getUser().getId(); } public AuthToken login(LoginRequest request) { User user = userRepository.findByEmail(request.getEmail()); if (!user.isActive()) { throw new NotValidFieldException("User not active", new BaseResponse(20003, MessageCode.USER_NOT_ACTIVE.getCode(), MessageCode.USER_NOT_ACTIVE.getDescription(), Map.of("userId", user.getId()))); } if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { LoginAnswer loginAnswer = new LoginAnswer(); loginAnswer.setFieldErrors(Arrays.asList(Map.of("password", request.getPassword()))); loginAnswer.setVerified(user.isEmailVerified()); loginAnswer.setActive(user.isActive()); int attempts = user.getFailedLoginAttempts() + 1; user.setFailedLoginAttempts(attempts); if (attempts >= 5) { user.setActive(false); blockUser(user); } userRepository.save(user); throw new NotValidFieldException("Invalid password", new BaseResponse(20003, MessageCode.AUTH_PASSWORD_NOT_MATCHES.getCode(), MessageCode.AUTH_PASSWORD_NOT_MATCHES.getDescription(), loginAnswer)); } user.setLastLoginAt(LocalDateTime.now()); user.setFailedLoginAttempts(0); user.setActive(true); User savedUser = userRepository.save(user); AuthToken authToken = new AuthToken(); authToken.setToken(generateAuthToken()); authToken.setUser(savedUser); return authTokenRepository.save(authToken); } @Transactional public void logout(String token) { authTokenRepository.findByToken(token) .ifPresent(authTokenRepository::delete); } @Transactional public AuthToken generateAuthToken(User user) { return authTokenRepository.save(genereateAuthToken(user)); } private AuthToken genereateAuthToken(User user) { AuthToken authToken = new AuthToken(); authToken.setToken(generateAuthToken()); authToken.setUser(user); return authToken; } private String generateAuthToken() { byte[] bytes = new byte[32]; secureRandom.nextBytes(bytes); return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); } private void createCompanyAccount(RegRequest request, User user) { Optional byInn = companyRepository.findByInn(request.getInn()); if (byInn.isPresent()) { throw new CompanyAlreadyExist("Company was already created"); } Company company = new Company(); company.setCompanyName(request.getCompanyName()); company.setInn(request.getInn()); TariffInfo companyTariff = tariffInfoService.createTariffInfo(TariffType.DEMO); company.setTariffInfo(companyTariff); companyRepository.save(company); tariffInfoService.addTariffInfo(companyTariff); user.setCompany(company); user.grantPermission(Permission.COMPANY_ADMIN_PERMISSIONS); } private void createIndividualAccount(User user) { TariffInfo personalTariff = tariffInfoService.createTariffInfo(TariffType.DEMO); tariffInfoService.addTariffInfo(personalTariff); user.setPersonalTariffInfo(personalTariff); user.grantPermission(Permission.DEFAULT_USER_PERMISSIONS); } private void createIndividualCompanyAccount(RegRequest request, User user) { String authToken = request.getAuthToken(); Long userIdByToken = authTokenRepository.findUserIdByToken(authToken); User companyUser = userRepository.findById(userIdByToken) .orElseThrow(() -> new EntityNotFoundException("Not found token: " + authToken)); Company company = companyUser.getCompany(); if (company == null) { throw new IllegalStateException("Not have company"); } TariffInfo tariffInfo = company.getTariffInfo(); if (tariffInfo == null) { throw new IllegalStateException("Company not have tariff"); } // Tariff tariff = tariffInfo.getTariff(); // Long maxUsers = tariff.getMaxUsers(); // long currentCountUsers = userRepository.countByCompanyId(company.getId()); // // if (currentCountUsers >= maxUsers) { // throw new NotValidFieldException("Max user is over.", new BaseResponse(20002, // MessageCode.USER_LIMIT_IS_OVER.getCode(), // MessageCode.USER_LIMIT_IS_OVER.getDescription(), Map.of( // "limit", maxUsers, "countUser", currentCountUsers))); // } user.setCompany(company); user.grantPermission(Permission.DEFAULT_USER_PERMISSIONS); } public void blockUser(User user) { Optional existing = userNotActiveRepository.findByUserId(user.getId()); if (existing.isEmpty()) { LocalDateTime now = LocalDateTime.now(); UserNotActive notActive = UserNotActive.builder() .user(user) .createdAt(now) .blockedUntil(now.plusMinutes(15)) .build(); userNotActiveRepository.save(notActive); } } }