Invalidate auth tokens
This commit is contained in:
@@ -14,6 +14,7 @@ import com.petshop.backend.repository.EmployeeStoreRepository;
|
|||||||
import com.petshop.backend.repository.UserRepository;
|
import com.petshop.backend.repository.UserRepository;
|
||||||
import com.petshop.backend.security.JwtUtil;
|
import com.petshop.backend.security.JwtUtil;
|
||||||
import com.petshop.backend.service.UserBusinessLinkageService;
|
import com.petshop.backend.service.UserBusinessLinkageService;
|
||||||
|
import com.petshop.backend.util.AuthenticationHelper;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@@ -22,8 +23,6 @@ import org.springframework.security.authentication.BadCredentialsException;
|
|||||||
import org.springframework.security.authentication.DisabledException;
|
import org.springframework.security.authentication.DisabledException;
|
||||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@@ -137,11 +136,7 @@ public class AuthController {
|
|||||||
|
|
||||||
@GetMapping("/me")
|
@GetMapping("/me")
|
||||||
public ResponseEntity<UserInfoResponse> getCurrentUser() {
|
public ResponseEntity<UserInfoResponse> getCurrentUser() {
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
User user = getAuthenticatedUser();
|
||||||
String username = authentication.getName();
|
|
||||||
|
|
||||||
User user = userRepository.findByUsername(username)
|
|
||||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
|
||||||
|
|
||||||
EmployeeStore employeeStore = resolveEmployeeStore(user);
|
EmployeeStore employeeStore = resolveEmployeeStore(user);
|
||||||
|
|
||||||
@@ -159,11 +154,8 @@ public class AuthController {
|
|||||||
|
|
||||||
@PutMapping("/me")
|
@PutMapping("/me")
|
||||||
public ResponseEntity<?> updateProfile(@Valid @RequestBody ProfileUpdateRequest request) {
|
public ResponseEntity<?> updateProfile(@Valid @RequestBody ProfileUpdateRequest request) {
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
User user = getAuthenticatedUser();
|
||||||
String username = authentication.getName();
|
boolean invalidateToken = false;
|
||||||
|
|
||||||
User user = userRepository.findByUsername(username)
|
|
||||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
|
||||||
|
|
||||||
if (request.getUsername() != null && !request.getUsername().equals(user.getUsername())) {
|
if (request.getUsername() != null && !request.getUsername().equals(user.getUsername())) {
|
||||||
if (userRepository.findByUsername(request.getUsername()).isPresent()) {
|
if (userRepository.findByUsername(request.getUsername()).isPresent()) {
|
||||||
@@ -172,6 +164,7 @@ public class AuthController {
|
|||||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||||
}
|
}
|
||||||
user.setUsername(request.getUsername());
|
user.setUsername(request.getUsername());
|
||||||
|
invalidateToken = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.getEmail() != null && !request.getEmail().equals(user.getEmail())) {
|
if (request.getEmail() != null && !request.getEmail().equals(user.getEmail())) {
|
||||||
@@ -189,6 +182,11 @@ public class AuthController {
|
|||||||
|
|
||||||
if (request.getPassword() != null && !request.getPassword().isEmpty()) {
|
if (request.getPassword() != null && !request.getPassword().isEmpty()) {
|
||||||
user.setPassword(passwordEncoder.encode(request.getPassword()));
|
user.setPassword(passwordEncoder.encode(request.getPassword()));
|
||||||
|
invalidateToken = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalidateToken) {
|
||||||
|
user.setTokenVersion(user.getTokenVersion() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
User updatedUser = userRepository.save(user);
|
User updatedUser = userRepository.save(user);
|
||||||
@@ -219,11 +217,7 @@ public class AuthController {
|
|||||||
|
|
||||||
@PostMapping("/me/avatar")
|
@PostMapping("/me/avatar")
|
||||||
public ResponseEntity<?> uploadAvatar(@RequestParam("avatar") MultipartFile file) {
|
public ResponseEntity<?> uploadAvatar(@RequestParam("avatar") MultipartFile file) {
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
User user = getAuthenticatedUser();
|
||||||
String username = authentication.getName();
|
|
||||||
|
|
||||||
User user = userRepository.findByUsername(username)
|
|
||||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
|
||||||
|
|
||||||
if (file.isEmpty()) {
|
if (file.isEmpty()) {
|
||||||
Map<String, String> error = new HashMap<>();
|
Map<String, String> error = new HashMap<>();
|
||||||
@@ -275,11 +269,7 @@ public class AuthController {
|
|||||||
|
|
||||||
@GetMapping("/me/avatar")
|
@GetMapping("/me/avatar")
|
||||||
public ResponseEntity<?> getAvatar() {
|
public ResponseEntity<?> getAvatar() {
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
User user = getAuthenticatedUser();
|
||||||
String username = authentication.getName();
|
|
||||||
|
|
||||||
User user = userRepository.findByUsername(username)
|
|
||||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
|
||||||
|
|
||||||
if (user.getAvatarUrl() == null || user.getAvatarUrl().isEmpty()) {
|
if (user.getAvatarUrl() == null || user.getAvatarUrl().isEmpty()) {
|
||||||
Map<String, String> error = new HashMap<>();
|
Map<String, String> error = new HashMap<>();
|
||||||
@@ -294,11 +284,7 @@ public class AuthController {
|
|||||||
|
|
||||||
@DeleteMapping("/me/avatar")
|
@DeleteMapping("/me/avatar")
|
||||||
public ResponseEntity<?> deleteAvatar() {
|
public ResponseEntity<?> deleteAvatar() {
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
User user = getAuthenticatedUser();
|
||||||
String username = authentication.getName();
|
|
||||||
|
|
||||||
User user = userRepository.findByUsername(username)
|
|
||||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
|
||||||
|
|
||||||
if (user.getAvatarUrl() != null && !user.getAvatarUrl().isEmpty()) {
|
if (user.getAvatarUrl() != null && !user.getAvatarUrl().isEmpty()) {
|
||||||
try {
|
try {
|
||||||
@@ -322,4 +308,12 @@ public class AuthController {
|
|||||||
response.put("note", "Token remains valid until expiration. Clear token from client storage.");
|
response.put("note", "Token remains valid until expiration. Clear token from client storage.");
|
||||||
return ResponseEntity.ok(response);
|
return ResponseEntity.ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private User getAuthenticatedUser() {
|
||||||
|
try {
|
||||||
|
return AuthenticationHelper.getAuthenticatedUser(userRepository);
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
throw new UsernameNotFoundException(ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,11 @@ import com.petshop.backend.repository.CustomerRepository;
|
|||||||
import com.petshop.backend.repository.UserRepository;
|
import com.petshop.backend.repository.UserRepository;
|
||||||
import com.petshop.backend.service.ChatRealtimeService;
|
import com.petshop.backend.service.ChatRealtimeService;
|
||||||
import com.petshop.backend.service.ChatService;
|
import com.petshop.backend.service.ChatService;
|
||||||
|
import com.petshop.backend.util.AuthenticationHelper;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@@ -37,9 +36,11 @@ public class ChatController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private User getCurrentUser() {
|
private User getCurrentUser() {
|
||||||
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
try {
|
||||||
return userRepository.findByUsername(userDetails.getUsername())
|
return AuthenticationHelper.getAuthenticatedUser(userRepository);
|
||||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
} catch (RuntimeException ex) {
|
||||||
|
throw new UsernameNotFoundException(ex.getMessage(), ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/conversations")
|
@PostMapping("/conversations")
|
||||||
|
|||||||
@@ -68,14 +68,23 @@ public class UserService {
|
|||||||
User user = userRepository.findById(id)
|
User user = userRepository.findById(id)
|
||||||
.orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + id));
|
.orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + id));
|
||||||
|
|
||||||
|
boolean invalidateToken =
|
||||||
|
!user.getUsername().equals(request.getUsername())
|
||||||
|
|| user.getRole() != request.getRole()
|
||||||
|
|| !user.getActive().equals(request.getActive() != null ? request.getActive() : true);
|
||||||
|
|
||||||
user.setUsername(request.getUsername());
|
user.setUsername(request.getUsername());
|
||||||
if (request.getPassword() != null && !request.getPassword().trim().isEmpty()) {
|
if (request.getPassword() != null && !request.getPassword().trim().isEmpty()) {
|
||||||
user.setPassword(passwordEncoder.encode(request.getPassword()));
|
user.setPassword(passwordEncoder.encode(request.getPassword()));
|
||||||
|
invalidateToken = true;
|
||||||
}
|
}
|
||||||
user.setFullName(request.getFullName());
|
user.setFullName(request.getFullName());
|
||||||
user.setEmail(request.getEmail());
|
user.setEmail(request.getEmail());
|
||||||
user.setRole(request.getRole());
|
user.setRole(request.getRole());
|
||||||
user.setActive(request.getActive() != null ? request.getActive() : true);
|
user.setActive(request.getActive() != null ? request.getActive() : true);
|
||||||
|
if (invalidateToken) {
|
||||||
|
user.setTokenVersion(user.getTokenVersion() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
user = userRepository.save(user);
|
user = userRepository.save(user);
|
||||||
return mapToResponse(user);
|
return mapToResponse(user);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.petshop.backend.entity.User;
|
|||||||
import com.petshop.backend.repository.CustomerRepository;
|
import com.petshop.backend.repository.CustomerRepository;
|
||||||
import com.petshop.backend.repository.EmployeeRepository;
|
import com.petshop.backend.repository.EmployeeRepository;
|
||||||
import com.petshop.backend.repository.UserRepository;
|
import com.petshop.backend.repository.UserRepository;
|
||||||
|
import com.petshop.backend.security.AppPrincipal;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@@ -13,11 +14,34 @@ import org.springframework.stereotype.Component;
|
|||||||
@Component
|
@Component
|
||||||
public class AuthenticationHelper {
|
public class AuthenticationHelper {
|
||||||
|
|
||||||
public static User getAuthenticatedUser(UserRepository userRepository) {
|
public static Authentication getAuthentication() {
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
if (authentication == null || !authentication.isAuthenticated()) {
|
if (authentication == null || !authentication.isAuthenticated()) {
|
||||||
throw new RuntimeException("No authenticated user found");
|
throw new RuntimeException("No authenticated user found");
|
||||||
}
|
}
|
||||||
|
return authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AppPrincipal getAuthenticatedPrincipal() {
|
||||||
|
Object principal = getAuthentication().getPrincipal();
|
||||||
|
if (principal instanceof AppPrincipal appPrincipal) {
|
||||||
|
return appPrincipal;
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Authenticated principal is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Long getAuthenticatedUserId() {
|
||||||
|
return getAuthenticatedPrincipal().getUserId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User getAuthenticatedUser(UserRepository userRepository) {
|
||||||
|
Authentication authentication = getAuthentication();
|
||||||
|
Object principal = authentication.getPrincipal();
|
||||||
|
|
||||||
|
if (principal instanceof AppPrincipal appPrincipal) {
|
||||||
|
return userRepository.findById(appPrincipal.getUserId())
|
||||||
|
.orElseThrow(() -> new RuntimeException("User not found: " + appPrincipal.getUserId()));
|
||||||
|
}
|
||||||
|
|
||||||
String username = authentication.getName();
|
String username = authentication.getName();
|
||||||
return userRepository.findByUsername(username)
|
return userRepository.findByUsername(username)
|
||||||
|
|||||||
Reference in New Issue
Block a user