NCP-3 add exception,add change password logic, fix checkauthtoken
This commit is contained in:
+2
-2
@@ -15,8 +15,8 @@ services:
|
||||
container_name: postgres
|
||||
|
||||
app:
|
||||
image: popovtsev/ncp
|
||||
# build: .
|
||||
# image: popovtsev/ncp
|
||||
build: .
|
||||
container_name: no_copy_app
|
||||
environment:
|
||||
POSTGRES_DB: no_copy_
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user