diff --git a/.env.example b/.env.example index a8e355c..fcbf755 100644 --- a/.env.example +++ b/.env.example @@ -14,6 +14,14 @@ JWT_SECRET=secret_key JWT_EXPIRATION= REFRESH_TOKEN_EXPIRATION= +# CORS Configuration (comma-separated) +CORS_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000 + +# Python Microservices Configuration +PYTHON_CALL_ANALYSIS_URL=http://localhost:5001 +PYTHON_FACIAL_RECOGNITION_URL=http://localhost:5002 + # Supabase Configurations SUPABASE_URL= SUPABASE_SERVICE_KEY= +SUPABASE_BUCKET=criminal-photos diff --git a/src/main/java/com/crimeLink/analyzer/config/CorsConfig.java b/src/main/java/com/crimeLink/analyzer/config/CorsConfig.java index 4acd274..18fb5e0 100644 --- a/src/main/java/com/crimeLink/analyzer/config/CorsConfig.java +++ b/src/main/java/com/crimeLink/analyzer/config/CorsConfig.java @@ -2,23 +2,28 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @Configuration public class CorsConfig { + @Value("${cors.allowed-origins:*}") + private String allowedOrigins; + @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); // Allow credentials (cookies, authorization headers, etc.) config.setAllowCredentials(true); // Use allowedOriginPatterns instead of allowedOrigins when credentials are enabled - config.setAllowedOriginPatterns(List.of("*")); + config.setAllowedOriginPatterns(parseAllowedOrigins(allowedOrigins)); config.setAllowedHeaders(List.of("*")); // Allow specific HTTP methods @@ -42,4 +47,21 @@ public CorsFilter corsFilter() { return new CorsFilter(source); } -} \ No newline at end of file + + private List parseAllowedOrigins(String raw) { + if (raw == null || raw.isBlank()) { + return List.of("*"); + } + + String[] parts = raw.split(","); + List origins = new ArrayList<>(); + for (String part : parts) { + String origin = part.trim(); + if (!origin.isEmpty()) { + origins.add(origin); + } + } + + return origins.isEmpty() ? List.of("*") : origins; + } +} diff --git a/src/main/java/com/crimeLink/analyzer/config/SecurityConfig.java b/src/main/java/com/crimeLink/analyzer/config/SecurityConfig.java index 8939518..e001370 100644 --- a/src/main/java/com/crimeLink/analyzer/config/SecurityConfig.java +++ b/src/main/java/com/crimeLink/analyzer/config/SecurityConfig.java @@ -1,9 +1,11 @@ package com.crimeLink.analyzer.config; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; @@ -37,6 +39,9 @@ public class SecurityConfig { @Autowired private UserDetailsService userDetailsService; + @Value("${cors.allowed-origins:*}") + private String allowedOrigins; + @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http @@ -128,8 +133,8 @@ public PasswordEncoder passwordEncoder() { public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); // Use allowedOriginPatterns for wildcard support with credentials - // For production, replace with specific origins - configuration.setAllowedOriginPatterns(List.of("*")); + // For production, replace with specific origins via CORS_ALLOWED_ORIGINS + configuration.setAllowedOriginPatterns(parseAllowedOrigins(allowedOrigins)); // Or use specific origins (recommended for production): // configuration.setAllowedOrigins(Arrays.asList( // "http://localhost:5173", @@ -148,4 +153,21 @@ public CorsConfigurationSource corsConfigurationSource() { return source; } -} \ No newline at end of file + + private List parseAllowedOrigins(String raw) { + if (raw == null || raw.isBlank()) { + return List.of("*"); + } + + String[] parts = raw.split(","); + List origins = new ArrayList<>(); + for (String part : parts) { + String origin = part.trim(); + if (!origin.isEmpty()) { + origins.add(origin); + } + } + + return origins.isEmpty() ? List.of("*") : origins; + } +} diff --git a/src/main/java/com/crimeLink/analyzer/controller/BulletController.java b/src/main/java/com/crimeLink/analyzer/controller/BulletController.java index 7f90c22..10a58fb 100644 --- a/src/main/java/com/crimeLink/analyzer/controller/BulletController.java +++ b/src/main/java/com/crimeLink/analyzer/controller/BulletController.java @@ -17,7 +17,6 @@ @RestController @RequestMapping("/api/bullet") @RequiredArgsConstructor -@CrossOrigin(origins = "*") public class BulletController { private final BulletService bulletService; @@ -94,4 +93,4 @@ private Map createErrorResponse(String message) { response.put("error", message); return response; } -} \ No newline at end of file +} diff --git a/src/main/java/com/crimeLink/analyzer/controller/CrimeReportController.java b/src/main/java/com/crimeLink/analyzer/controller/CrimeReportController.java index ee18472..660f81b 100644 --- a/src/main/java/com/crimeLink/analyzer/controller/CrimeReportController.java +++ b/src/main/java/com/crimeLink/analyzer/controller/CrimeReportController.java @@ -4,7 +4,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -25,7 +24,6 @@ @RestController @AllArgsConstructor @RequestMapping("/api/crime-reports") -@CrossOrigin("*") public class CrimeReportController { private final CrimeReportService crimeReportService; diff --git a/src/main/java/com/crimeLink/analyzer/controller/DutyRecommendationController.java b/src/main/java/com/crimeLink/analyzer/controller/DutyRecommendationController.java index 042fc82..0ccff60 100644 --- a/src/main/java/com/crimeLink/analyzer/controller/DutyRecommendationController.java +++ b/src/main/java/com/crimeLink/analyzer/controller/DutyRecommendationController.java @@ -9,8 +9,6 @@ @RestController @RequestMapping("/api/duty-recommendations") -@CrossOrigin(origins = "*") - public class DutyRecommendationController { private final DutyRecommendationService recommendationService; diff --git a/src/main/java/com/crimeLink/analyzer/controller/DutyScheduleController.java b/src/main/java/com/crimeLink/analyzer/controller/DutyScheduleController.java index 7135074..1de333e 100644 --- a/src/main/java/com/crimeLink/analyzer/controller/DutyScheduleController.java +++ b/src/main/java/com/crimeLink/analyzer/controller/DutyScheduleController.java @@ -16,7 +16,6 @@ @RestController @RequestMapping("/api/duty-schedules") -@CrossOrigin(origins = "*") public class DutyScheduleController { private final DutyScheduleService dutyService; diff --git a/src/main/java/com/crimeLink/analyzer/controller/LeaveController.java b/src/main/java/com/crimeLink/analyzer/controller/LeaveController.java index ceedb52..81681a7 100644 --- a/src/main/java/com/crimeLink/analyzer/controller/LeaveController.java +++ b/src/main/java/com/crimeLink/analyzer/controller/LeaveController.java @@ -11,7 +11,6 @@ @RestController @RequestMapping("/api/leaves") @RequiredArgsConstructor -@CrossOrigin(origins = "*", allowedHeaders = "*") public class LeaveController { private final LeaveService leaveService; diff --git a/src/main/java/com/crimeLink/analyzer/controller/MobileDutyController.java b/src/main/java/com/crimeLink/analyzer/controller/MobileDutyController.java index bc64f5f..ef02c1d 100644 --- a/src/main/java/com/crimeLink/analyzer/controller/MobileDutyController.java +++ b/src/main/java/com/crimeLink/analyzer/controller/MobileDutyController.java @@ -15,12 +15,6 @@ @RestController @RequestMapping("/api/duties") @RequiredArgsConstructor -@CrossOrigin( - origins = "*", - methods = {RequestMethod.GET, RequestMethod.OPTIONS}, - allowedHeaders = "*", - maxAge = 3600 -) public class MobileDutyController { private final MobileDutyService mobileDutyService; @@ -66,4 +60,4 @@ public ResponseEntity> getDutyDetailsByDate( return ResponseEntity.internalServerError().build(); } } -} \ No newline at end of file +} diff --git a/src/main/java/com/crimeLink/analyzer/controller/SafetyLocationController.java b/src/main/java/com/crimeLink/analyzer/controller/SafetyLocationController.java index db859c8..a5aaa38 100644 --- a/src/main/java/com/crimeLink/analyzer/controller/SafetyLocationController.java +++ b/src/main/java/com/crimeLink/analyzer/controller/SafetyLocationController.java @@ -3,7 +3,6 @@ import java.util.List; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -15,7 +14,6 @@ @RestController @RequestMapping("/api/safety-locations") -@CrossOrigin public class SafetyLocationController { @Autowired diff --git a/src/main/java/com/crimeLink/analyzer/controller/VehicleController.java b/src/main/java/com/crimeLink/analyzer/controller/VehicleController.java index a5f1139..6d0ad1e 100644 --- a/src/main/java/com/crimeLink/analyzer/controller/VehicleController.java +++ b/src/main/java/com/crimeLink/analyzer/controller/VehicleController.java @@ -17,7 +17,6 @@ @RestController @RequestMapping("/api/vehicles") -@CrossOrigin(origins = {"http://localhost:5173", "http://localhost:3000"}) public class VehicleController { @Autowired diff --git a/src/main/java/com/crimeLink/analyzer/controller/WeaponController.java b/src/main/java/com/crimeLink/analyzer/controller/WeaponController.java index 76f1a44..d68e00c 100644 --- a/src/main/java/com/crimeLink/analyzer/controller/WeaponController.java +++ b/src/main/java/com/crimeLink/analyzer/controller/WeaponController.java @@ -17,7 +17,6 @@ @RestController @RequestMapping("/api/weapon") @RequiredArgsConstructor -@CrossOrigin(origins = "*") public class WeaponController { private final WeaponService weaponService; @@ -94,4 +93,4 @@ private Map createErrorResponse(String message) { response.put("error", message); return response; } -} \ No newline at end of file +} diff --git a/src/main/java/com/crimeLink/analyzer/controller/WeaponIssueController.java b/src/main/java/com/crimeLink/analyzer/controller/WeaponIssueController.java index 73f9f08..ac79c7f 100644 --- a/src/main/java/com/crimeLink/analyzer/controller/WeaponIssueController.java +++ b/src/main/java/com/crimeLink/analyzer/controller/WeaponIssueController.java @@ -17,7 +17,6 @@ @RestController @RequestMapping("/api/weapon") @RequiredArgsConstructor -@CrossOrigin(origins = "*") public class WeaponIssueController { private final WeaponIssueService weaponIssueService; @@ -94,4 +93,4 @@ private Map createSuccessResponse(String message) { response.put("message", message); return response; } -} \ No newline at end of file +} diff --git a/src/main/java/com/crimeLink/analyzer/service/JwtService.java b/src/main/java/com/crimeLink/analyzer/service/JwtService.java index 0bd918f..c33919f 100644 --- a/src/main/java/com/crimeLink/analyzer/service/JwtService.java +++ b/src/main/java/com/crimeLink/analyzer/service/JwtService.java @@ -8,7 +8,9 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; +import java.nio.charset.StandardCharsets; import java.security.Key; +import java.security.MessageDigest; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -83,7 +85,22 @@ private Claims extractAllClaims(String token) { } private Key getSignInKey() { - byte[] keyBytes = Decoders.BASE64.decode(secretKey); + byte[] keyBytes; + try { + keyBytes = Decoders.BASE64.decode(secretKey); + } catch (IllegalArgumentException ex) { + // Fallback for non-base64 secrets: derive a stable 32-byte key + byte[] raw = secretKey.getBytes(StandardCharsets.UTF_8); + if (raw.length < 32) { + try { + keyBytes = MessageDigest.getInstance("SHA-256").digest(raw); + } catch (Exception e) { + keyBytes = raw; + } + } else { + keyBytes = raw; + } + } return Keys.hmacShaKeyFor(keyBytes); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index abf4b5e..978b588 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -34,6 +34,9 @@ jwt.secret=${JWT_SECRET} jwt.expiration=${JWT_EXPIRATION:900000} jwt.refresh-expiration=${REFRESH_TOKEN_EXPIRATION:604800000} +# CORS Configuration +cors.allowed-origins=${CORS_ALLOWED_ORIGINS:http://localhost:5173,http://localhost:3000} + # Python Microservices Configuration python.call-analysis.url=${PYTHON_CALL_ANALYSIS_URL:http://localhost:5001} python.facial-recognition.url=${PYTHON_FACIAL_RECOGNITION_URL:http://localhost:5002}