Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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
26 changes: 24 additions & 2 deletions src/main/java/com/crimeLink/analyzer/config/CorsConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -42,4 +47,21 @@ public CorsFilter corsFilter() {

return new CorsFilter(source);
}
}

private List<String> parseAllowedOrigins(String raw) {
if (raw == null || raw.isBlank()) {
return List.of("*");
}

String[] parts = raw.split(",");
List<String> origins = new ArrayList<>();
for (String part : parts) {
String origin = part.trim();
if (!origin.isEmpty()) {
origins.add(origin);
}
}

return origins.isEmpty() ? List.of("*") : origins;
}
}
28 changes: 25 additions & 3 deletions src/main/java/com/crimeLink/analyzer/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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",
Expand All @@ -148,4 +153,21 @@ public CorsConfigurationSource corsConfigurationSource() {

return source;
}
}

private List<String> parseAllowedOrigins(String raw) {
if (raw == null || raw.isBlank()) {
return List.of("*");
}

String[] parts = raw.split(",");
List<String> origins = new ArrayList<>();
for (String part : parts) {
String origin = part.trim();
if (!origin.isEmpty()) {
origins.add(origin);
}
}

return origins.isEmpty() ? List.of("*") : origins;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
@RestController
@RequestMapping("/api/bullet")
@RequiredArgsConstructor
@CrossOrigin(origins = "*")
public class BulletController {

private final BulletService bulletService;
Expand Down Expand Up @@ -94,4 +93,4 @@ private Map<String, String> createErrorResponse(String message) {
response.put("error", message);
return response;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -25,7 +24,6 @@
@RestController
@AllArgsConstructor
@RequestMapping("/api/crime-reports")
@CrossOrigin("*")
public class CrimeReportController {

private final CrimeReportService crimeReportService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@

@RestController
@RequestMapping("/api/duty-recommendations")
@CrossOrigin(origins = "*")

public class DutyRecommendationController {

private final DutyRecommendationService recommendationService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

@RestController
@RequestMapping("/api/duty-schedules")
@CrossOrigin(origins = "*")
public class DutyScheduleController {

private final DutyScheduleService dutyService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
@RestController
@RequestMapping("/api/leaves")
@RequiredArgsConstructor
@CrossOrigin(origins = "*", allowedHeaders = "*")
public class LeaveController {

private final LeaveService leaveService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -66,4 +60,4 @@ public ResponseEntity<List<DutyDetailDTO>> getDutyDetailsByDate(
return ResponseEntity.internalServerError().build();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -15,7 +14,6 @@

@RestController
@RequestMapping("/api/safety-locations")
@CrossOrigin
public class SafetyLocationController {

@Autowired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

@RestController
@RequestMapping("/api/vehicles")
@CrossOrigin(origins = {"http://localhost:5173", "http://localhost:3000"})
public class VehicleController {

@Autowired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
@RestController
@RequestMapping("/api/weapon")
@RequiredArgsConstructor
@CrossOrigin(origins = "*")
public class WeaponController {

private final WeaponService weaponService;
Expand Down Expand Up @@ -94,4 +93,4 @@ private Map<String, String> createErrorResponse(String message) {
response.put("error", message);
return response;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
@RestController
@RequestMapping("/api/weapon")
@RequiredArgsConstructor
@CrossOrigin(origins = "*")
public class WeaponIssueController {

private final WeaponIssueService weaponIssueService;
Expand Down Expand Up @@ -94,4 +93,4 @@ private Map<String, String> createSuccessResponse(String message) {
response.put("message", message);
return response;
}
}
}
19 changes: 18 additions & 1 deletion src/main/java/com/crimeLink/analyzer/service/JwtService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
3 changes: 3 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
Loading