103 lines
4.4 KiB
Java
103 lines
4.4 KiB
Java
package com.smartoffice.auth;
|
|
|
|
import com.smartoffice.common.ApiException;
|
|
import com.smartoffice.user.Role;
|
|
import com.smartoffice.user.User;
|
|
import com.smartoffice.user.UserDto;
|
|
import com.smartoffice.user.UserRepository;
|
|
import com.smartoffice.user.UserStatus;
|
|
import jakarta.transaction.Transactional;
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
import org.springframework.security.authentication.AuthenticationManager;
|
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
import org.springframework.security.core.Authentication;
|
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import java.time.Instant;
|
|
|
|
@Service
|
|
public class AuthService {
|
|
private final UserRepository userRepository;
|
|
private final PasswordEncoder passwordEncoder;
|
|
private final AuthenticationManager authenticationManager;
|
|
private final JwtService jwtService;
|
|
private final int maxAttempts;
|
|
private final int lockMinutes;
|
|
|
|
public AuthService(UserRepository userRepository,
|
|
PasswordEncoder passwordEncoder,
|
|
AuthenticationManager authenticationManager,
|
|
JwtService jwtService,
|
|
@Value("${app.security.lockout.max-attempts}") int maxAttempts,
|
|
@Value("${app.security.lockout.lock-minutes}") int lockMinutes) {
|
|
this.userRepository = userRepository;
|
|
this.passwordEncoder = passwordEncoder;
|
|
this.authenticationManager = authenticationManager;
|
|
this.jwtService = jwtService;
|
|
this.maxAttempts = maxAttempts;
|
|
this.lockMinutes = lockMinutes;
|
|
}
|
|
|
|
@Transactional
|
|
public AuthResponse login(LoginRequest request) {
|
|
User user = userRepository.findByUsername(request.getUsername())
|
|
.orElseThrow(() -> new ApiException(401, "Invalid credentials"));
|
|
|
|
if (user.getStatus() == UserStatus.DISABLED) {
|
|
throw new ApiException(403, "Account disabled");
|
|
}
|
|
if (user.getLockedUntil() != null && user.getLockedUntil().isAfter(Instant.now())) {
|
|
throw new ApiException(423, "Account locked until " + user.getLockedUntil());
|
|
}
|
|
if (user.getStatus() == UserStatus.LOCKED && user.getLockedUntil() == null) {
|
|
throw new ApiException(423, "Account locked");
|
|
}
|
|
if (user.getLockedUntil() != null && user.getLockedUntil().isBefore(Instant.now())) {
|
|
user.setLockedUntil(null);
|
|
user.setStatus(UserStatus.ACTIVE);
|
|
user.setFailedLoginAttempts(0);
|
|
userRepository.save(user);
|
|
}
|
|
|
|
try {
|
|
Authentication authentication = authenticationManager.authenticate(
|
|
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
|
|
UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
|
|
user.setFailedLoginAttempts(0);
|
|
user.setLockedUntil(null);
|
|
user.setLastLoginAt(Instant.now());
|
|
user.setStatus(UserStatus.ACTIVE);
|
|
userRepository.save(user);
|
|
String token = jwtService.generateToken(principal);
|
|
return new AuthResponse(token, UserDto.from(user));
|
|
} catch (Exception ex) {
|
|
int attempts = user.getFailedLoginAttempts() + 1;
|
|
user.setFailedLoginAttempts(attempts);
|
|
if (attempts >= maxAttempts) {
|
|
user.setLockedUntil(Instant.now().plusSeconds(lockMinutes * 60L));
|
|
user.setStatus(UserStatus.LOCKED);
|
|
}
|
|
userRepository.save(user);
|
|
throw new ApiException(401, "Invalid credentials");
|
|
}
|
|
}
|
|
|
|
@Transactional
|
|
public UserDto register(RegisterRequest request) {
|
|
if (userRepository.existsByUsername(request.getUsername())) {
|
|
throw new ApiException(409, "Username already exists");
|
|
}
|
|
User user = new User();
|
|
user.setUsername(request.getUsername());
|
|
user.setPasswordHash(passwordEncoder.encode(request.getPassword()));
|
|
user.setFullName(request.getFullName());
|
|
user.setEmail(request.getEmail());
|
|
user.setPhone(request.getPhone());
|
|
user.setRole(Role.EMPLOYEE);
|
|
user.setStatus(UserStatus.ACTIVE);
|
|
userRepository.save(user);
|
|
return UserDto.from(user);
|
|
}
|
|
}
|