Public Access
Cleaned up the architecture
This commit is contained in:
1 parent
cc24c43ab8
commit
161381ebe5
17 files changed
+297
-46
No files matched your search
@@ -1,13 +1,12 @@
|
||||
package auth_service.domain.login.controller;
|
||||
|
||||
import auth_service.domain.login.controller.dtos.LoginRequest;
|
||||
import auth_service.domain.login.controller.dtos.LoginResponse;
|
||||
import auth_service.domain.login.repository.UserRepository;
|
||||
import auth_service.domain.login.controller.dtos.request.LoginRequest;
|
||||
import auth_service.domain.login.controller.dtos.response.LoginResponse;
|
||||
import auth_service.domain.login.controller.mapper.ApiMapper;
|
||||
import auth_service.domain.login.service.LoginService;
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -21,8 +20,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RequiredArgsConstructor
|
||||
public class LoginController {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final LoginService loginService;
|
||||
|
||||
/**
|
||||
* Authenticates a user based on email and password.
|
||||
@@ -34,13 +32,8 @@ public class LoginController {
|
||||
@Timed(value = "auth.login.requests.time", description = "Time taken to process login requests")
|
||||
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) {
|
||||
|
||||
return userRepository.findByEmail(request.email())
|
||||
.filter(user -> passwordEncoder.matches(request.password(), user.getPassword()))
|
||||
.map(user -> ResponseEntity.ok(
|
||||
new LoginResponse("Login successful", user.getEmail())
|
||||
))
|
||||
.orElseGet(() -> ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(
|
||||
new LoginResponse("Invalid credentials", null)
|
||||
));
|
||||
var user = ApiMapper.toModel(request);
|
||||
loginService.login(user);
|
||||
return ResponseEntity.ok(new LoginResponse("Login successful", user.getEmail()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package auth_service.domain.login.controller;
|
||||
|
||||
import auth_service.domain.login.controller.dtos.response.ErrorResponse;
|
||||
import auth_service.domain.login.exception.InvalidCredentialsException;
|
||||
import auth_service.domain.login.exception.UserNotFoundException;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@RestControllerAdvice
|
||||
@Order(value = 1)
|
||||
@Slf4j
|
||||
public class LoginExceptionController {
|
||||
private static @NonNull ResponseEntity<ErrorResponse> response500(ErrorResponse body) {
|
||||
log.error(body.message(), body.code());
|
||||
return ResponseEntity.status(500)
|
||||
.contentType(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.body(body);
|
||||
}
|
||||
|
||||
private static @NonNull ResponseEntity<ErrorResponse> response400(ErrorResponse body) {
|
||||
return ResponseEntity.status(400)
|
||||
.contentType(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.body(body);
|
||||
}
|
||||
|
||||
/*
|
||||
######## CLIENT ERRORS (500-599)
|
||||
*/
|
||||
|
||||
/*
|
||||
######## CLIENT ERRORS (400-499)
|
||||
*/
|
||||
@ExceptionHandler(UserNotFoundException.class)
|
||||
public ResponseEntity<ErrorResponse> clientError(UserNotFoundException ex) {
|
||||
var body = new ErrorResponse("USER_NOT_FOUND", ex.getMessage());
|
||||
return response400(body);
|
||||
}
|
||||
|
||||
@ExceptionHandler(InvalidCredentialsException.class)
|
||||
public ResponseEntity<ErrorResponse> clientError(InvalidCredentialsException ex) {
|
||||
var body = new ErrorResponse("INVALID_CREDENTIALS", ex.getMessage());
|
||||
return response400(body);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package auth_service.domain.login.controller.dtos;
|
||||
|
||||
/**
|
||||
* DTO (Data Transfer Object) representing the incoming login request.
|
||||
*/
|
||||
public record LoginRequest(String email, String password) {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package auth_service.domain.login.controller.dtos.request;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* DTO (Data Transfer Object) representing the incoming login request.
|
||||
*/
|
||||
public record LoginRequest(@NotNull String email, @NotNull @NotBlank String password) {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package auth_service.domain.login.controller.dtos.response;
|
||||
|
||||
public record ErrorResponse(String code, String message) {
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package auth_service.domain.login.controller.dtos;
|
||||
package auth_service.domain.login.controller.dtos.response;
|
||||
|
||||
/**
|
||||
* DTO representing the outgoing login response.
|
||||
@@ -0,0 +1,13 @@
|
||||
package auth_service.domain.login.controller.mapper;
|
||||
|
||||
import auth_service.domain.login.controller.dtos.request.LoginRequest;
|
||||
import auth_service.domain.login.model.User;
|
||||
|
||||
public abstract class ApiMapper {
|
||||
private ApiMapper() {
|
||||
}
|
||||
|
||||
public static User toModel(LoginRequest loginRequest) {
|
||||
return User.builder().email(loginRequest.email()).encryptedPassword(loginRequest.password()).build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package auth_service.domain.login.exception;
|
||||
|
||||
public class InvalidCredentialsException extends RuntimeException {
|
||||
public InvalidCredentialsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package auth_service.domain.login.exception;
|
||||
|
||||
public class UserNotFoundException extends RuntimeException {
|
||||
public UserNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package auth_service.domain.login.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Builder
|
||||
@Data
|
||||
public class User {
|
||||
private long id;
|
||||
private String email;
|
||||
private String encryptedPassword;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package auth_service.domain.login.persistence;
|
||||
|
||||
import auth_service.domain.login.model.User;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Repository for database operations regarding the User entity.
|
||||
*/
|
||||
public interface UserRepository {
|
||||
/**
|
||||
* Finds a user by their email address.
|
||||
* * @param email The email to search for
|
||||
*
|
||||
* @return An Optional containing the user if found
|
||||
*/
|
||||
Optional<User> findByEmail(String email);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package auth_service.domain.login.persistence;
|
||||
|
||||
import auth_service.domain.login.model.User;
|
||||
import auth_service.domain.login.persistence.entities.UserEntity;
|
||||
import auth_service.domain.login.persistence.mapper.EntityMapper;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public class UserRepositoryImpl implements UserRepository {
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
|
||||
@Override
|
||||
public Optional<User> findByEmail(String email) {
|
||||
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<UserEntity> query = cb.createQuery(UserEntity.class);
|
||||
Root<UserEntity> user = query.from(UserEntity.class);
|
||||
|
||||
// Build the query: SELECT u FROM UserEntity u WHERE u.email = :email
|
||||
query.select(user).where(cb.equal(user.get("email"), email));
|
||||
|
||||
TypedQuery<UserEntity> typedQuery = entityManager.createQuery(query);
|
||||
|
||||
return typedQuery.getResultStream()
|
||||
.findFirst()
|
||||
.map(EntityMapper::mapToModel);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package auth_service.domain.login.model;
|
||||
package auth_service.domain.login.persistence.entities;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
@@ -0,0 +1,22 @@
|
||||
package auth_service.domain.login.persistence.mapper;
|
||||
|
||||
import auth_service.domain.login.model.User;
|
||||
import auth_service.domain.login.persistence.entities.UserEntity;
|
||||
|
||||
public abstract class EntityMapper {
|
||||
|
||||
private EntityMapper() {
|
||||
}
|
||||
|
||||
public static User mapToModel(UserEntity entity) {
|
||||
User model = User.builder().build();
|
||||
|
||||
if (entity != null) {
|
||||
model.setId(entity.getId());
|
||||
model.setEmail(entity.getEmail());
|
||||
model.setEncryptedPassword(entity.getPassword());
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package auth_service.domain.login.repository;
|
||||
|
||||
import auth_service.domain.login.model.UserEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Repository for database operations regarding the User entity.
|
||||
*/
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<UserEntity, Long> {
|
||||
|
||||
/**
|
||||
* Finds a user by their email address.
|
||||
* * @param email The email to search for
|
||||
*
|
||||
* @return An Optional containing the user if found
|
||||
*/
|
||||
Optional<UserEntity> findByEmail(String email);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package auth_service.domain.login.service;
|
||||
|
||||
import auth_service.domain.login.exception.InvalidCredentialsException;
|
||||
import auth_service.domain.login.exception.UserNotFoundException;
|
||||
import auth_service.domain.login.model.User;
|
||||
import auth_service.domain.login.persistence.UserRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class LoginService {
|
||||
private final UserRepository userRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public String login(User userToLogin) {
|
||||
var userEntity = userRepository.findByEmail(userToLogin.getEmail()).orElseThrow(() -> new UserNotFoundException("User not found with given email"));
|
||||
var loginSuccessful = passwordEncoder.matches(userToLogin.getEncryptedPassword(), userEntity.getEncryptedPassword());
|
||||
if (!loginSuccessful) {
|
||||
throw new InvalidCredentialsException("Invalid credentials");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user