diff --git a/src/main/java/org/example/alfs/config/PasswordConfig.java b/src/main/java/org/example/alfs/config/PasswordConfig.java new file mode 100644 index 0000000..e319407 --- /dev/null +++ b/src/main/java/org/example/alfs/config/PasswordConfig.java @@ -0,0 +1,16 @@ +package org.example.alfs.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class PasswordConfig { + + // Makes password encoder available in the whole app + @Bean + public PasswordEncoder passwordEncoder(){ + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/org/example/alfs/controllers/AuthController.java b/src/main/java/org/example/alfs/controllers/AuthController.java new file mode 100644 index 0000000..fdf7562 --- /dev/null +++ b/src/main/java/org/example/alfs/controllers/AuthController.java @@ -0,0 +1,39 @@ +package org.example.alfs.controllers; + +import org.example.alfs.dto.auth.LoginRequestDTO; +import org.example.alfs.dto.auth.LoginResponseDTO; +import org.example.alfs.entities.User; +import org.example.alfs.services.AuthService; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/auth") +public class AuthController { + + private final AuthService authService; + + public AuthController(AuthService authService) { + this.authService = authService; + } + + /** + * Handles user login by validating credentials and returning user details. + */ + @PostMapping("/login") + public LoginResponseDTO login(@Valid @RequestBody LoginRequestDTO request) { + + User user = authService.login( + request.getUsername(), + request.getPassword() + ); + + return new LoginResponseDTO( + user.getUsername(), + user.getRole().name() + ); + } +} diff --git a/src/main/java/org/example/alfs/dto/auth/LoginRequestDTO.java b/src/main/java/org/example/alfs/dto/auth/LoginRequestDTO.java new file mode 100644 index 0000000..e318715 --- /dev/null +++ b/src/main/java/org/example/alfs/dto/auth/LoginRequestDTO.java @@ -0,0 +1,17 @@ +package org.example.alfs.dto.auth; + +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class LoginRequestDTO { + + @NotBlank(message = "Username is required") + private String username; + + @NotBlank(message = "Password is required") + private String password; + +} diff --git a/src/main/java/org/example/alfs/dto/auth/LoginResponseDTO.java b/src/main/java/org/example/alfs/dto/auth/LoginResponseDTO.java new file mode 100644 index 0000000..350e6eb --- /dev/null +++ b/src/main/java/org/example/alfs/dto/auth/LoginResponseDTO.java @@ -0,0 +1,12 @@ +package org.example.alfs.dto.auth; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class LoginResponseDTO { + + private String username; + private String role; +} diff --git a/src/main/java/org/example/alfs/entities/User.java b/src/main/java/org/example/alfs/entities/User.java index 43f85d3..3925335 100644 --- a/src/main/java/org/example/alfs/entities/User.java +++ b/src/main/java/org/example/alfs/entities/User.java @@ -27,6 +27,14 @@ public class User { private String passwordHash; @Enumerated(EnumType.STRING) + @Column(nullable = false) private Role role; + @PrePersist + public void prePersist() { + if (role == null) { + role = Role.REPORTER; + } + } + } diff --git a/src/main/java/org/example/alfs/repositories/UserRepository.java b/src/main/java/org/example/alfs/repositories/UserRepository.java index 2b82879..d31ffe9 100644 --- a/src/main/java/org/example/alfs/repositories/UserRepository.java +++ b/src/main/java/org/example/alfs/repositories/UserRepository.java @@ -7,4 +7,5 @@ public interface UserRepository extends JpaRepository { + Optional findByUsername(String username); } diff --git a/src/main/java/org/example/alfs/services/AuthService.java b/src/main/java/org/example/alfs/services/AuthService.java new file mode 100644 index 0000000..c227f2f --- /dev/null +++ b/src/main/java/org/example/alfs/services/AuthService.java @@ -0,0 +1,40 @@ +package org.example.alfs.services; + +import org.example.alfs.entities.User; +import org.example.alfs.repositories.UserRepository; +import org.springframework.http.HttpStatus; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.web.server.ResponseStatusException; + +@Service +public class AuthService { + + private final PasswordEncoder passwordEncoder; + private final UserRepository userRepository; + + public AuthService(PasswordEncoder passwordEncoder, UserRepository userRepository) { + this.passwordEncoder = passwordEncoder; + this.userRepository = userRepository; + } + + /** + * Authenticates a user by verifying username and password. + */ + public User login(String username, String password) { + + User user = userRepository.findByUsername(username) + .orElseThrow(() -> new ResponseStatusException( + HttpStatus.UNAUTHORIZED, + "Invalid username or password" + )); + if (!passwordEncoder.matches(password, user.getPasswordHash())) { + throw new ResponseStatusException( + HttpStatus.UNAUTHORIZED, + "Invalid username or password" + ); + } + + return user; + } +}