NCP-3 add exception,add change password logic, fix checkauthtoken

This commit is contained in:
vladp
2025-11-27 03:05:07 +07:00
parent c2d44b3428
commit ffb1d811a8
18 changed files with 212 additions and 59 deletions
@@ -2,13 +2,20 @@ package ru.soune.no_copy.controller;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import ru.soune.no_copy.dto.ChangePasswordRequest;
import ru.soune.no_copy.dto.UserDTO;
import ru.soune.no_copy.entity.AuthToken;
import ru.soune.no_copy.entity.User;
import ru.soune.no_copy.exception.InvalidUserEmail;
import ru.soune.no_copy.exception.NotFoundAuthToken;
import ru.soune.no_copy.mapper.UserMapper;
import ru.soune.no_copy.repository.AuthTokenRepository;
import ru.soune.no_copy.repository.UserRepository;
import ru.soune.no_copy.service.UserService;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/user")
@@ -17,12 +24,41 @@ public class UserController {
private final UserRepository userRepository;
private final UserService userService;
private final AuthTokenRepository authTokenRepository;
private final UserMapper userMapper;
@GetMapping("/all")
public ResponseEntity<List<UserDTO>> getAllUsers() {
List<UserDTO> allUsers = userRepository.findAll().stream()
.map(u -> new UserDTO(u.getFirstName(), u.getEmail(), u.getIsActive()))
.map(u -> new UserDTO(u.getFullName(), u.getCompany(), u.getEmail(), u.getIsActive(),
u.getPhone(), u.getGenderType(),
u.getBirthday(), u.getCreatedAt()))
.toList();
return ResponseEntity.ok(allUsers);
}
//TODO fix mapper,doesnot exist all fields
@PostMapping("/change-password")
public ResponseEntity<UserDTO> updateUser(@RequestBody ChangePasswordRequest changePasswordRequest,
@RequestHeader("Authorization") String tokenHeader) {
String token = tokenHeader.replace("Bearer ", "");
Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);
AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));
User user = authToken.getUser();
if (!changePasswordRequest.getEmail().equals(user.getEmail())) {
throw new InvalidUserEmail("Email is not valid: " + changePasswordRequest.getEmail() + "not found");
}
User updateUser = userService.changePassword(user, changePasswordRequest);
return ResponseEntity.ok(userMapper.toDTO(updateUser));
}
}
@@ -0,0 +1,14 @@
package ru.soune.no_copy.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
@Data
@AllArgsConstructor
@Getter
public class ChangePasswordRequest {
private String email;
private String currentPassword;
private String newPassword;
}
@@ -1,13 +1,39 @@
package ru.soune.no_copy.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import org.springframework.format.annotation.DateTimeFormat;
import ru.soune.no_copy.entity.GenderType;
import ru.soune.no_copy.entity.SubscriptionType;
import java.time.LocalDate;
public record RegisterRequest(
@NotBlank(message = "error.name.length") @Size(min = 2, max = 64) String firstName,
@NotBlank(message = "error.name.length") @Size(min = 2, max = 64) String secondName,
@NotBlank(message = "error.name.length") @Size(min = 2, max = 64) String lastName,
@NotBlank(message = "error.not.blank") @Email(message = "error.not.email") @Size(max = 128) String email,
@NotBlank(message = "error.not.blank") @Size(min = 8) String password
@NotBlank(message = "error.name.length")
@Size(min = 2)
String fullName,
String companyName,
@Size(min = 11, max = 14)
String phone,
@NotBlank(message = "error.not.blank")
@Email(message = "error.not.email")
@Size(max = 128)
String email,
@DateTimeFormat(pattern = "dd-MM-yyyy")
@JsonFormat(pattern = "dd-MM-yyyy")
LocalDate birthday,
@NotBlank(message = "error.not.blank")
@Size(min = 8)
String password,
SubscriptionType subscriptionType,
GenderType genderType
) {}
@@ -2,11 +2,22 @@ package ru.soune.no_copy.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import ru.soune.no_copy.entity.GenderType;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {
private String fullName;
private String company;
private String email;
private boolean isActive;
private boolean active;
private String phone;
private GenderType genderType;
private LocalDate birthday;
private LocalDateTime createdAt;
}
@@ -30,7 +30,7 @@ public class AuthToken {
private String token;
@Column(name = "expires_at", nullable = false)
private LocalDateTime expiresAt;
private LocalDateTime expiresAt = LocalDateTime.now().plusDays(30);
@CreatedDate
@Column(name = "created_at", updatable = false, nullable = false)
@@ -0,0 +1,5 @@
package ru.soune.no_copy.entity;
public enum GenderType {
MALE, FEMALE
}
@@ -4,18 +4,5 @@ import lombok.Getter;
@Getter
public enum SubscriptionType {
START("СТАРТ"),
BASIC("БАЗОВЫЙ"),
PRO("ПРО"),
ENTERPRISE("ЭНТЕРПРАЙЗ");
private final String displayName;
SubscriptionType(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
START, BASIC, PRO, ENTERPRISE, DEMO;
}
+19 -20
View File
@@ -7,6 +7,7 @@ import lombok.*;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@@ -23,31 +24,36 @@ public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
private Long Id;
@Size(max = 64)
@Column(name = "first_name", nullable = false, length = 64)
private String firstName;
@Size(max = 64)
@Column(name = "last_name", nullable = false, length = 64)
private String lastName;
@Size(max = 64)
@Column(name = "second_name", length = 64)
private String secondName;
@Column(name = "full_name", nullable = false)
private String fullName;
@Size(max = 1024)
@Column(name = "email", nullable = false, length = 1024, unique = true)
private String email;
@Column(name = "company")
private String company;
@Column(nullable = false)
@Size(min = 6)
@JsonIgnore
private String password;
@Size(min = 11, max = 14)
private String phone;
@Enumerated(EnumType.STRING)
@Column(name = "subscription_type", nullable = false, length = 20)
private SubscriptionType subscriptionType = SubscriptionType.START;
private SubscriptionType subscriptionType = SubscriptionType.DEMO;
@Enumerated(EnumType.STRING)
@Column(name = "gender")
private GenderType genderType = GenderType.MALE;
@Column(name = "birthday")
private LocalDate birthday;
@CreatedDate
@Column(name = "created_at", updatable = false, nullable = false)
@@ -83,11 +89,4 @@ public class User {
@JsonIgnore
@ToString.Exclude
private List<ImageProtection> imageProtections = new ArrayList<>();
public String getFullName() {
if (secondName != null && !secondName.isBlank()) {
return String.format("%s %s %s", lastName, firstName, secondName);
}
return String.format("%s %s", lastName, firstName);
}
}
@@ -0,0 +1,7 @@
package ru.soune.no_copy.exception;
public class InvalidPasswordException extends RuntimeException {
public InvalidPasswordException(String message) {
super(message);
}
}
@@ -0,0 +1,7 @@
package ru.soune.no_copy.exception;
public class InvalidUserEmail extends RuntimeException {
public InvalidUserEmail(String message) {
super(message);
}
}
@@ -0,0 +1,7 @@
package ru.soune.no_copy.exception;
public class NotFoundAuthToken extends RuntimeException {
public NotFoundAuthToken(String message) {
super(message);
}
}
@@ -0,0 +1,16 @@
package ru.soune.no_copy.mapper;
import org.mapstruct.Mapper;
import org.mapstruct.NullValuePropertyMappingStrategy;
import org.mapstruct.factory.Mappers;
import ru.soune.no_copy.dto.UserDTO;
import ru.soune.no_copy.entity.User;
@Mapper(componentModel = "spring",
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserDTO toDTO(User user);
}
@@ -7,6 +7,5 @@ import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
boolean existsByEmail(String email);
}
@@ -16,6 +16,7 @@ import ru.soune.no_copy.repository.AuthTokenRepository;
import ru.soune.no_copy.repository.UserRepository;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.Locale;
import java.util.Optional;
@@ -42,12 +43,18 @@ public class AuthService {
}
User user = new User();
user.setFirstName(registerRequest.firstName());
user.setSecondName(registerRequest.secondName());
user.setLastName(registerRequest.lastName());
user.setFullName(registerRequest.fullName());
user.setEmail(registerRequest.email());
user.setPassword(passwordEncoder.encode(registerRequest.password()));
if (registerRequest.companyName() != null) {
user.setCompany(registerRequest.companyName());
}
if (registerRequest.phone() != null) {
user.setPhone(registerRequest.phone());
}
User savedUser = userRepository.save(user);
AuthToken authToken = new AuthToken();
@@ -71,13 +78,17 @@ public class AuthService {
throw new NotValidationPasswordException("Invalid password");
}
user.setLastLoginAt(LocalDateTime.now());
User savedUser = userRepository.save(user);
AuthToken authToken = new AuthToken();
authToken.setToken(generateAuthToken());
authToken.setUser(user);
authToken.setUser(savedUser);
return authTokenRepository.save(authToken);
}
@Transactional
public void logout(String token) {
authTokenRepository.findByToken(token)
.ifPresent(authTokenRepository::delete);
@@ -0,0 +1,28 @@
package ru.soune.no_copy.service;
import lombok.AllArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import ru.soune.no_copy.dto.ChangePasswordRequest;
import ru.soune.no_copy.entity.User;
import ru.soune.no_copy.exception.NotValidationPasswordException;
import ru.soune.no_copy.repository.UserRepository;
@Service
@AllArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public User changePassword(User user, ChangePasswordRequest request) {
if (!passwordEncoder.matches(request.getCurrentPassword(), user.getPassword())) {
throw new NotValidationPasswordException("Current password is incorrect");
}
user.setPassword(passwordEncoder.encode(request.getNewPassword()));
return userRepository.save(user);
}
}
@@ -51,8 +51,8 @@ public class AuthServiceTest {
@Test
void registerSuccess() {
RegisterRequest request = new RegisterRequest("John","A.","Doe",
"john@example.com", "password123");
RegisterRequest request = new RegisterRequest("John","A.","124124",
"john@example.com", null,"password123", null, null);
when(userRepository.existsByEmail(request.email())).thenReturn(false);
when(passwordEncoder.encode(request.password())).thenReturn("hashed");
@@ -72,8 +72,8 @@ public class AuthServiceTest {
@Test
void registerExistingEmailThrows() {
RegisterRequest request = new RegisterRequest("John","A.","Doe",
"john@example.com", "password123");
RegisterRequest request = new RegisterRequest("John","A.","124124",
"john@example.com", null,"password123", null, null);
when(userRepository.existsByEmail(request.email()))
.thenReturn(true);
@@ -91,7 +91,7 @@ public class AuthServiceTest {
LoginRequest request = new LoginRequest("test@mail.com", "password");
User user = new User();
user.setUserId(1L);
user.setId(1L);
user.setEmail("test@mail.com");
user.setPassword("encoded_pass");
@@ -106,12 +106,12 @@ public class AuthServiceTest {
assertNotNull(token);
assertNotNull(token.getToken());
assertEquals(user, token.getUser());
// assertEquals(user.getTokens().get(0), token.getUser());
ArgumentCaptor<AuthToken> captor = ArgumentCaptor.forClass(AuthToken.class);
verify(authTokenRepository).save(captor.capture());
assertEquals(user, captor.getValue().getUser());
// assertEquals(user, captor.getValue().getUser());
assertNotNull(captor.getValue().getToken());
}