Harden startup config
This commit is contained in:
@@ -1,7 +1,5 @@
|
|||||||
package com.petshop.backend.config;
|
package com.petshop.backend.config;
|
||||||
|
|
||||||
import com.petshop.backend.entity.User;
|
|
||||||
import com.petshop.backend.repository.UserRepository;
|
|
||||||
import com.petshop.backend.security.AppPrincipal;
|
import com.petshop.backend.security.AppPrincipal;
|
||||||
import com.petshop.backend.service.ActivityLogService;
|
import com.petshop.backend.service.ActivityLogService;
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
@@ -18,15 +16,13 @@ import org.springframework.web.filter.OncePerRequestFilter;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@Order(Ordered.LOWEST_PRECEDENCE - 20)
|
@Order(Ordered.LOWEST_PRECEDENCE - 20)
|
||||||
public class ActivityLoggingFilter extends OncePerRequestFilter {
|
public class ActivityLoggingFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
private final UserRepository userRepository;
|
|
||||||
private final ActivityLogService activityLogService;
|
private final ActivityLogService activityLogService;
|
||||||
|
|
||||||
public ActivityLoggingFilter(UserRepository userRepository, ActivityLogService activityLogService) {
|
public ActivityLoggingFilter(ActivityLogService activityLogService) {
|
||||||
this.userRepository = userRepository;
|
|
||||||
this.activityLogService = activityLogService;
|
this.activityLogService = activityLogService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,12 +69,7 @@ public class ActivityLoggingFilter extends OncePerRequestFilter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
User user = userRepository.findById(userId).orElse(null);
|
|
||||||
if (user == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String activity = String.format("%s %s -> %d", request.getMethod(), request.getRequestURI(), response.getStatus());
|
String activity = String.format("%s %s -> %d", request.getMethod(), request.getRequestURI(), response.getStatus());
|
||||||
activityLogService.record(user, activity);
|
activityLogService.record(userId, activity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.petshop.backend.config;
|
package com.petshop.backend.config;
|
||||||
|
|
||||||
import org.flywaydb.core.Flyway;
|
import org.flywaydb.core.Flyway;
|
||||||
import org.flywaydb.core.api.MigrationVersion;
|
|
||||||
import org.springframework.context.ApplicationContextInitializer;
|
import org.springframework.context.ApplicationContextInitializer;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
@@ -38,10 +37,7 @@ public class FlywayContextInitializer implements ApplicationContextInitializer<C
|
|||||||
Flyway flyway = Flyway.configure()
|
Flyway flyway = Flyway.configure()
|
||||||
.dataSource(url, username, password)
|
.dataSource(url, username, password)
|
||||||
.locations(locations)
|
.locations(locations)
|
||||||
.baselineOnMigrate(environment.getProperty("spring.flyway.baseline-on-migrate", Boolean.class, false))
|
|
||||||
.baselineVersion(MigrationVersion.fromVersion(environment.getProperty("spring.flyway.baseline-version", "1")))
|
|
||||||
.load();
|
.load();
|
||||||
flyway.repair();
|
|
||||||
flyway.migrate();
|
flyway.migrate();
|
||||||
return;
|
return;
|
||||||
} catch (RuntimeException ex) {
|
} catch (RuntimeException ex) {
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ public class AuthController {
|
|||||||
User savedUser = userRepository.save(user);
|
User savedUser = userRepository.save(user);
|
||||||
|
|
||||||
String token = jwtUtil.generateToken(savedUser);
|
String token = jwtUtil.generateToken(savedUser);
|
||||||
activityLogService.record(savedUser, "POST /api/v1/auth/register -> 201");
|
activityLogService.record(savedUser.getId(), "POST /api/v1/auth/register -> 201");
|
||||||
|
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body(new RegisterResponse(
|
return ResponseEntity.status(HttpStatus.CREATED).body(new RegisterResponse(
|
||||||
savedUser.getId(),
|
savedUser.getId(),
|
||||||
@@ -124,7 +124,7 @@ public class AuthController {
|
|||||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||||
|
|
||||||
String token = jwtUtil.generateToken(user);
|
String token = jwtUtil.generateToken(user);
|
||||||
activityLogService.record(user, "POST /api/v1/auth/login -> 200");
|
activityLogService.record(user.getId(), "POST /api/v1/auth/login -> 200");
|
||||||
|
|
||||||
return ResponseEntity.ok(new LoginResponse(
|
return ResponseEntity.ok(new LoginResponse(
|
||||||
token,
|
token,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.petshop.backend.entity.User;
|
|||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
import io.jsonwebtoken.security.Keys;
|
import io.jsonwebtoken.security.Keys;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -23,6 +24,16 @@ public class JwtUtil {
|
|||||||
@Value("${jwt.expiration}")
|
@Value("${jwt.expiration}")
|
||||||
private Long expiration;
|
private Long expiration;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
void validateConfiguration() {
|
||||||
|
if (secret == null || secret.isBlank()) {
|
||||||
|
throw new IllegalStateException("JWT_SECRET must be configured");
|
||||||
|
}
|
||||||
|
if (secret.getBytes(StandardCharsets.UTF_8).length < 32) {
|
||||||
|
throw new IllegalStateException("JWT_SECRET must be at least 32 bytes long");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private SecretKey getSigningKey() {
|
private SecretKey getSigningKey() {
|
||||||
return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
|
return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,13 +28,16 @@ public class ActivityLogService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void record(User user, String activity) {
|
public void record(Long userId, String activity) {
|
||||||
if (user == null || activity == null || activity.isBlank()) {
|
if (userId == null || activity == null || activity.isBlank()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
User managedUser = userRepository.findById(user.getId()).orElse(user);
|
User managedUser = userRepository.findById(userId).orElse(null);
|
||||||
|
if (managedUser == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
StoreLocation store = managedUser.getPrimaryStore();
|
StoreLocation store = managedUser.getPrimaryStore();
|
||||||
ActivityLog entry = new ActivityLog();
|
ActivityLog entry = new ActivityLog();
|
||||||
entry.setUser(managedUser);
|
entry.setUser(managedUser);
|
||||||
@@ -50,6 +53,13 @@ public class ActivityLogService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void record(User user, String activity) {
|
||||||
|
if (user == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
record(user.getId(), activity);
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public List<ActivityLogResponse> getLogs(int limit) {
|
public List<ActivityLogResponse> getLogs(int limit) {
|
||||||
return activityLogRepository.findRecent(PageRequest.of(0, limit)).stream().map(this::toResponse).toList();
|
return activityLogRepository.findRecent(PageRequest.of(0, limit)).stream().map(this::toResponse).toList();
|
||||||
|
|||||||
2
backend/src/main/resources/application-local.yml
Normal file
2
backend/src/main/resources/application-local.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
jwt:
|
||||||
|
secret: ${JWT_SECRET:local-development-jwt-secret-change-me-please-123456}
|
||||||
@@ -33,9 +33,7 @@ spring:
|
|||||||
open-in-view: false
|
open-in-view: false
|
||||||
|
|
||||||
flyway:
|
flyway:
|
||||||
enabled: true
|
enabled: false
|
||||||
baseline-on-migrate: true
|
|
||||||
baseline-version: 1
|
|
||||||
|
|
||||||
server:
|
server:
|
||||||
port: ${SERVER_PORT:8080}
|
port: ${SERVER_PORT:8080}
|
||||||
@@ -50,7 +48,7 @@ springdoc:
|
|||||||
path: /swagger-ui
|
path: /swagger-ui
|
||||||
|
|
||||||
jwt:
|
jwt:
|
||||||
secret: ${JWT_SECRET:change_me_please_make_this_at_least_32_characters_long_for_security}
|
secret: ${JWT_SECRET}
|
||||||
expiration: ${JWT_EXPIRATION:86400000}
|
expiration: ${JWT_EXPIRATION:86400000}
|
||||||
|
|
||||||
stripe:
|
stripe:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Made by **Group 2**, Shiv, Nikitha, Alex, Harkamal.
|
|||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- IntelliJ IDEA (Community or Ultimate)
|
- IntelliJ IDEA (Community or Ultimate)
|
||||||
- Java 17+
|
- Java 25+
|
||||||
- Maven (handled through IntelliJ)
|
- Maven (handled through IntelliJ)
|
||||||
- Docker and Docker Compose (for the local MySQL container)
|
- Docker and Docker Compose (for the local MySQL container)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user