mirror of
https://gitlab.com/etc404/software-engineering-project.git
synced 2026-05-10 20:52:58 +00:00
Update 9 files
- /demo/src/main/java/com/example/demo/config/SecurityConfig.java - /demo/src/main/java/com/example/demo/controller/AuthController.java - /demo/src/main/java/com/example/demo/dto/LoginRequest.java - /demo/src/main/java/com/example/demo/dto/RecipeCreateRequest.java - /demo/src/main/java/com/example/demo/dto/RegisterRequest.java - /demo/src/main/java/com/example/demo/exception/BadRequestException.java - /demo/src/main/java/com/example/demo/exception/ErrorResponse.java - /demo/src/main/java/com/example/demo/exception/GlobalExceptionHandler.java - /demo/src/main/java/com/example/demo/exception/NotFoundException.java
This commit is contained in:
@@ -1,5 +1,50 @@
|
|||||||
package com.example.demo.config;
|
package com.example.demo.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
|
||||||
|
http
|
||||||
|
// Uses your CorsConfigurationSource bean from CorsConfig.java
|
||||||
|
.cors(Customizer.withDefaults())
|
||||||
|
|
||||||
|
// For now, disable CSRF so you can POST from a separate frontend easily.
|
||||||
|
// If you later use cookies/sessions, revisit CSRF.
|
||||||
|
.csrf(csrf -> csrf.disable())
|
||||||
|
|
||||||
|
// Auth rules
|
||||||
|
.authorizeHttpRequests(auth -> auth
|
||||||
|
// Allow health check + auth endpoints without login
|
||||||
|
.requestMatchers("/api/health").permitAll()
|
||||||
|
.requestMatchers("/api/auth/**").permitAll()
|
||||||
|
|
||||||
|
// Everything else requires authentication (you can loosen this later)
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Temporary: enables basic auth popup in browser tools.
|
||||||
|
// Later you’ll likely switch to JWT or session login.
|
||||||
|
.httpBasic(Customizer.withDefaults());
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,28 @@
|
|||||||
package com.example.demo.controller;
|
package com.example.demo.controller;
|
||||||
|
|
||||||
|
import com.example.demo.dto.LoginRequest;
|
||||||
|
import com.example.demo.dto.RegisterRequest;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/auth")
|
||||||
public class AuthController {
|
public class AuthController {
|
||||||
|
|
||||||
|
// TEMP: Register endpoint (service logic added later)
|
||||||
|
@PostMapping("/register")
|
||||||
|
public ResponseEntity<?> register(@Valid @RequestBody RegisterRequest request) {
|
||||||
|
|
||||||
|
// For now just return what was sent (test validation first)
|
||||||
|
return ResponseEntity.ok("User registered: " + request.getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEMP: Login endpoint
|
||||||
|
@PostMapping("/login")
|
||||||
|
public ResponseEntity<?> login(@Valid @RequestBody LoginRequest request) {
|
||||||
|
|
||||||
|
return ResponseEntity.ok("Login attempt for: " + request.getUsernameOrEmail());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,18 @@
|
|||||||
package com.example.demo.dto;
|
package com.example.demo.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
public class LoginRequest {
|
public class LoginRequest {
|
||||||
|
|
||||||
|
@NotBlank(message = "usernameOrEmail is required")
|
||||||
|
private String usernameOrEmail;
|
||||||
|
|
||||||
|
@NotBlank(message = "password is required")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
public String getUsernameOrEmail() { return usernameOrEmail; }
|
||||||
|
public void setUsernameOrEmail(String usernameOrEmail) { this.usernameOrEmail = usernameOrEmail; }
|
||||||
|
|
||||||
|
public String getPassword() { return password; }
|
||||||
|
public void setPassword(String password) { this.password = password; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,53 @@
|
|||||||
package com.example.demo.dto;
|
package com.example.demo.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.PositiveOrZero;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class RecipeCreateRequest {
|
public class RecipeCreateRequest {
|
||||||
|
|
||||||
|
@NotBlank(message = "title is required")
|
||||||
|
@Size(max = 100, message = "title must be 100 characters or less")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Size(max = 1000, message = "description must be 1000 characters or less")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@PositiveOrZero(message = "prepTimeMinutes must be 0 or greater")
|
||||||
|
private int prepTimeMinutes;
|
||||||
|
|
||||||
|
@PositiveOrZero(message = "cookTimeMinutes must be 0 or greater")
|
||||||
|
private int cookTimeMinutes;
|
||||||
|
|
||||||
|
@PositiveOrZero(message = "servings must be 0 or greater")
|
||||||
|
private int servings;
|
||||||
|
|
||||||
|
@NotNull(message = "ingredients list is required")
|
||||||
|
private List<IngredientDto> ingredients;
|
||||||
|
|
||||||
|
@NotNull(message = "steps list is required")
|
||||||
|
private List<StepDto> steps;
|
||||||
|
|
||||||
|
public String getTitle() { return title; }
|
||||||
|
public void setTitle(String title) { this.title = title; }
|
||||||
|
|
||||||
|
public String getDescription() { return description; }
|
||||||
|
public void setDescription(String description) { this.description = description; }
|
||||||
|
|
||||||
|
public int getPrepTimeMinutes() { return prepTimeMinutes; }
|
||||||
|
public void setPrepTimeMinutes(int prepTimeMinutes) { this.prepTimeMinutes = prepTimeMinutes; }
|
||||||
|
|
||||||
|
public int getCookTimeMinutes() { return cookTimeMinutes; }
|
||||||
|
public void setCookTimeMinutes(int cookTimeMinutes) { this.cookTimeMinutes = cookTimeMinutes; }
|
||||||
|
|
||||||
|
public int getServings() { return servings; }
|
||||||
|
public void setServings(int servings) { this.servings = servings; }
|
||||||
|
|
||||||
|
public List<IngredientDto> getIngredients() { return ingredients; }
|
||||||
|
public void setIngredients(List<IngredientDto> ingredients) { this.ingredients = ingredients; }
|
||||||
|
|
||||||
|
public List<StepDto> getSteps() { return steps; }
|
||||||
|
public void setSteps(List<StepDto> steps) { this.steps = steps; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,29 @@
|
|||||||
package com.example.demo.dto;
|
package com.example.demo.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Email;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
|
||||||
public class RegisterRequest {
|
public class RegisterRequest {
|
||||||
|
|
||||||
|
@NotBlank(message = "username is required")
|
||||||
|
@Size(min = 3, max = 30, message = "username must be 3-30 characters")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@NotBlank(message = "email is required")
|
||||||
|
@Email(message = "email must be valid")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@NotBlank(message = "password is required")
|
||||||
|
@Size(min = 8, max = 100, message = "password must be at least 8 characters")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
public String getUsername() { return username; }
|
||||||
|
public void setUsername(String username) { this.username = username; }
|
||||||
|
|
||||||
|
public String getEmail() { return email; }
|
||||||
|
public void setEmail(String email) { this.email = email; }
|
||||||
|
|
||||||
|
public String getPassword() { return password; }
|
||||||
|
public void setPassword(String password) { this.password = password; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.example.demo.exception;
|
package com.example.demo.exception;
|
||||||
|
|
||||||
public class BadRequestException {
|
@SuppressWarnings("serial")
|
||||||
|
public class BadRequestException extends RuntimeException {
|
||||||
}
|
public BadRequestException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,25 @@
|
|||||||
package com.example.demo.exception;
|
package com.example.demo.exception;
|
||||||
|
|
||||||
public class ErrorResponse {
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
}
|
public class ErrorResponse {
|
||||||
|
private LocalDateTime timestamp;
|
||||||
|
private int status;
|
||||||
|
private String error;
|
||||||
|
private String message;
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
public ErrorResponse(LocalDateTime timestamp, int status, String error, String message, String path) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.status = status;
|
||||||
|
this.error = error;
|
||||||
|
this.message = message;
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getTimestamp() { return timestamp; }
|
||||||
|
public int getStatus() { return status; }
|
||||||
|
public String getError() { return error; }
|
||||||
|
public String getMessage() { return message; }
|
||||||
|
public String getPath() { return path; }
|
||||||
|
}
|
||||||
@@ -1,5 +1,57 @@
|
|||||||
package com.example.demo.exception;
|
package com.example.demo.exception;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ControllerAdvice
|
||||||
public class GlobalExceptionHandler {
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
@ExceptionHandler(NotFoundException.class)
|
||||||
|
public ResponseEntity<ErrorResponse> handleNotFound(NotFoundException ex, HttpServletRequest request) {
|
||||||
|
return buildError(HttpStatus.NOT_FOUND, ex.getMessage(), request.getRequestURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(BadRequestException.class)
|
||||||
|
public ResponseEntity<ErrorResponse> handleBadRequest(BadRequestException ex, HttpServletRequest request) {
|
||||||
|
return buildError(HttpStatus.BAD_REQUEST, ex.getMessage(), request.getRequestURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles @Valid validation failures from DTOs
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
|
||||||
|
String msg = ex.getBindingResult()
|
||||||
|
.getFieldErrors()
|
||||||
|
.stream()
|
||||||
|
.map(err -> err.getField() + ": " + err.getDefaultMessage())
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
|
||||||
|
return buildError(HttpStatus.BAD_REQUEST, msg, request.getRequestURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback for anything you didn't explicitly handle
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public ResponseEntity<ErrorResponse> handleGeneric(Exception ex, HttpServletRequest request) {
|
||||||
|
// In production you'd avoid returning raw exception messages.
|
||||||
|
return buildError(HttpStatus.INTERNAL_SERVER_ERROR, "Unexpected error occurred", request.getRequestURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseEntity<ErrorResponse> buildError(HttpStatus status, String message, String path) {
|
||||||
|
ErrorResponse body = new ErrorResponse(
|
||||||
|
LocalDateTime.now(),
|
||||||
|
status.value(),
|
||||||
|
status.getReasonPhrase(),
|
||||||
|
message,
|
||||||
|
path
|
||||||
|
);
|
||||||
|
return ResponseEntity.status(status).body(body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.example.demo.exception;
|
package com.example.demo.exception;
|
||||||
|
|
||||||
public class NotFoundException {
|
@SuppressWarnings("serial")
|
||||||
|
public class NotFoundException extends RuntimeException {
|
||||||
|
public NotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user