Skip to content

Commit 2353f9f

Browse files
committed
resolve merge confilcr in security config
2 parents 98ac1b3 + 8bc5461 commit 2353f9f

28 files changed

Lines changed: 1229 additions & 62 deletions

.github/workflows/codeql.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: "CodeQL"
22

33
on:
44
push:
5-
branches: [ "main" ]
5+
branches: [ "main", "Dev" ]
66
pull_request:
7-
branches: [ "main" ]
7+
branches: [ "main", "Dev" ]
88
schedule:
99
- cron: "33 19 * * 4"
1010

src/main/java/com/crimeLink/analyzer/config/SecurityConfig.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,15 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
5151
.requestMatchers("/api/facial/health").permitAll() // ML service health check
5252
.requestMatchers("/api/call-analysis/health").permitAll() // ML service health check
5353

54-
// ML Service endpoints - Investigator role only
54+
// ML Service endpoints
5555
.requestMatchers("/api/call-analysis/**").hasRole("Investigator")
56+
.requestMatchers("/api/facial/register").hasAnyRole("Investigator", "OIC")
57+
.requestMatchers("/api/facial/criminals").hasAnyRole("Investigator", "OIC")
5658
.requestMatchers("/api/facial/**").hasRole("Investigator")
59+
60+
// Criminal CRUD (direct DB, no Python)
61+
.requestMatchers("/api/criminals/**").hasAnyRole("Investigator", "OIC")
62+
.requestMatchers("/api/criminals").hasAnyRole("Investigator", "OIC")
5763

5864
.requestMatchers("/api/database/**").permitAll()
5965
.requestMatchers("/api/test").permitAll()
@@ -79,9 +85,10 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
7985
.requestMatchers("/api/weapon/**").hasRole("OIC")
8086
.requestMatchers("/api/weapon-issue/**").hasRole("OIC")
8187

82-
// Admin/OIC routes (officer data, locations, users)
83-
.requestMatchers("/api/users/field-officers").hasAnyRole("Admin", "OIC", "FieldOfficer", "Investigator")
84-
.requestMatchers("/api/admin/**").hasAnyRole("OIC", "Admin","Investigator")
88+
// Admin/OIC/Investigator routes (officer data, locations, users)
89+
.requestMatchers("/api/users/field-officers").hasAnyRole("Admin", "OIC", "Investigator")
90+
.requestMatchers("/api/admin/officers/*/locations/**").hasAnyRole("Admin", "OIC", "Investigator")
91+
.requestMatchers("/api/admin/**").hasAnyRole("OIC", "Admin")
8592

8693
.anyRequest().authenticated())
8794
.exceptionHandling(exception -> exception
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package com.crimeLink.analyzer.controller;
2+
3+
import com.crimeLink.analyzer.dto.BulletAddDTO;
4+
import com.crimeLink.analyzer.dto.BulletResponseDTO;
5+
import com.crimeLink.analyzer.dto.BulletUpdateDTO;
6+
import com.crimeLink.analyzer.entity.Bullet;
7+
import com.crimeLink.analyzer.service.BulletService;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.http.HttpStatus;
10+
import org.springframework.http.ResponseEntity;
11+
import org.springframework.web.bind.annotation.*;
12+
13+
import java.util.HashMap;
14+
import java.util.List;
15+
import java.util.Map;
16+
17+
@RestController
18+
@RequestMapping("/api/bullet")
19+
@RequiredArgsConstructor
20+
@CrossOrigin(origins = "*")
21+
public class BulletController {
22+
23+
private final BulletService bulletService;
24+
25+
@PostMapping("/add-bullet")
26+
public ResponseEntity<?> addBullet(@RequestBody BulletAddDTO dto) {
27+
try {
28+
Bullet bullet = bulletService.addBullet(dto);
29+
return ResponseEntity.status(HttpStatus.CREATED).body(bullet);
30+
} catch (RuntimeException e) {
31+
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
32+
.body(createErrorResponse(e.getMessage()));
33+
} catch (Exception e) {
34+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
35+
.body(createErrorResponse("An unexpected error occurred"));
36+
}
37+
}
38+
39+
@PutMapping("/bullet-update/{bulletId}")
40+
public ResponseEntity<?> updateBullet(
41+
@PathVariable Integer bulletId,
42+
@RequestBody BulletUpdateDTO dto) {
43+
try {
44+
Bullet bullet = bulletService.updateBullet(bulletId, dto);
45+
return ResponseEntity.ok(bullet);
46+
} catch (RuntimeException e) {
47+
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
48+
.body(createErrorResponse(e.getMessage()));
49+
} catch (Exception e) {
50+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
51+
.body(createErrorResponse("An unexpected error occurred"));
52+
}
53+
}
54+
55+
@GetMapping("/all")
56+
public ResponseEntity<?> getAllBullets() {
57+
try {
58+
List<Bullet> bullets = bulletService.getAllBullets();
59+
return ResponseEntity.ok(bullets);
60+
} catch (Exception e) {
61+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
62+
.body(createErrorResponse("Failed to fetch bullets"));
63+
}
64+
}
65+
66+
@GetMapping("/all-with-details")
67+
public ResponseEntity<?> getAllBulletsWithDetails() {
68+
try {
69+
List<BulletResponseDTO> bullets = bulletService.getAllBulletsWithDetails();
70+
return ResponseEntity.ok(bullets);
71+
} catch (Exception e) {
72+
e.printStackTrace();
73+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
74+
.body(createErrorResponse("Failed to fetch bullets with details: " + e.getMessage()));
75+
}
76+
}
77+
78+
@GetMapping("/{bulletId}")
79+
public ResponseEntity<?> getBulletById(@PathVariable Integer bulletId) {
80+
try {
81+
Bullet bullet = bulletService.getBulletById(bulletId);
82+
return ResponseEntity.ok(bullet);
83+
} catch (RuntimeException e) {
84+
return ResponseEntity.status(HttpStatus.NOT_FOUND)
85+
.body(createErrorResponse(e.getMessage()));
86+
} catch (Exception e) {
87+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
88+
.body(createErrorResponse("Failed to fetch bullet"));
89+
}
90+
}
91+
92+
private Map<String, String> createErrorResponse(String message) {
93+
Map<String, String> response = new HashMap<>();
94+
response.put("error", message);
95+
return response;
96+
}
97+
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
package com.crimeLink.analyzer.controller;
2+
3+
import com.crimeLink.analyzer.service.CriminalService;
4+
import com.crimeLink.analyzer.util.LogSanitizer;
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.springframework.http.ResponseEntity;
8+
import org.springframework.web.bind.annotation.*;
9+
import org.springframework.web.multipart.MultipartFile;
10+
11+
import java.util.List;
12+
import java.util.Map;
13+
import java.util.Optional;
14+
15+
/**
16+
* REST Controller for Criminal record management (CRUD).
17+
* Operates directly against the database via JPA — coordinates with Python ML for embeddings.
18+
*
19+
* Endpoints:
20+
* - POST /api/criminals - Create new criminal (with photo upload + embedding)
21+
* - GET /api/criminals - List all criminals
22+
* - GET /api/criminals/{id} - Get criminal details
23+
* - PUT /api/criminals/{id} - Update criminal profile (with optional photo)
24+
* - DELETE /api/criminals/{id} - Delete criminal record
25+
*/
26+
@RestController
27+
@RequestMapping("/api/criminals")
28+
@RequiredArgsConstructor
29+
@Slf4j
30+
public class CriminalController {
31+
32+
private final CriminalService criminalService;
33+
34+
/**
35+
* Create a new criminal record with optional photo.
36+
*/
37+
@PostMapping
38+
public ResponseEntity<?> createCriminal(
39+
@RequestParam("name") String name,
40+
@RequestParam("nic") String nic,
41+
@RequestParam(value = "photo", required = false) MultipartFile photo,
42+
@RequestParam(value = "risk_level", required = false) String riskLevel,
43+
@RequestParam(value = "crime_history", required = false) String crimeHistory,
44+
@RequestParam(value = "address", required = false) String address,
45+
@RequestParam(value = "contact_number", required = false) String contactNumber,
46+
@RequestParam(value = "secondary_contact", required = false) String secondaryContact,
47+
@RequestParam(value = "date_of_birth", required = false) String dateOfBirth,
48+
@RequestParam(value = "gender", required = false) String gender,
49+
@RequestParam(value = "alias", required = false) String alias,
50+
@RequestParam(value = "status", required = false) String status) {
51+
52+
try {
53+
log.info("Criminal registration requested: name={}, nic={}",
54+
LogSanitizer.sanitize(name), LogSanitizer.sanitize(nic));
55+
56+
if (name == null || name.trim().isEmpty()) {
57+
return ResponseEntity.badRequest().body(Map.of("error", "Name is required"));
58+
}
59+
if (nic == null || nic.trim().isEmpty()) {
60+
return ResponseEntity.badRequest().body(Map.of("error", "NIC is required"));
61+
}
62+
63+
Map<String, Object> result = criminalService.createCriminal(
64+
name.trim(), nic.trim(), riskLevel, crimeHistory,
65+
address, contactNumber, secondaryContact,
66+
dateOfBirth, gender, alias, status, photo);
67+
68+
return ResponseEntity.ok(result);
69+
70+
} catch (Exception e) {
71+
log.error("Criminal registration failed: {}", e.getMessage());
72+
return ResponseEntity.internalServerError()
73+
.body(Map.of("error", "Registration failed: " + e.getMessage()));
74+
}
75+
}
76+
77+
/**
78+
* Get all criminals (summary list).
79+
*/
80+
@GetMapping
81+
public ResponseEntity<?> getAllCriminals() {
82+
try {
83+
List<Map<String, Object>> criminals = criminalService.getAllCriminals();
84+
return ResponseEntity.ok(criminals);
85+
} catch (Exception e) {
86+
log.error("Failed to fetch criminals: {}", e.getMessage());
87+
return ResponseEntity.internalServerError()
88+
.body(Map.of("error", "Failed to fetch criminals: " + e.getMessage()));
89+
}
90+
}
91+
92+
/**
93+
* Get full details for a specific criminal.
94+
*/
95+
@GetMapping("/{criminalId}")
96+
public ResponseEntity<?> getCriminalDetails(@PathVariable String criminalId) {
97+
try {
98+
Optional<Map<String, Object>> result = criminalService.getCriminalDetails(criminalId);
99+
if (result.isEmpty()) {
100+
return ResponseEntity.status(404)
101+
.body(Map.of("error", "Criminal not found: " + LogSanitizer.sanitize(criminalId)));
102+
}
103+
return ResponseEntity.ok(result.get());
104+
} catch (Exception e) {
105+
log.error("Failed to fetch criminal {}: {}", LogSanitizer.sanitize(criminalId), e.getMessage());
106+
return ResponseEntity.internalServerError()
107+
.body(Map.of("error", "Failed to fetch criminal details: " + e.getMessage()));
108+
}
109+
}
110+
111+
/**
112+
* Update an existing criminal's profile data (with optional photo change).
113+
*/
114+
@PutMapping("/{criminalId}")
115+
public ResponseEntity<?> updateCriminal(
116+
@PathVariable String criminalId,
117+
@RequestParam(value = "name", required = false) String name,
118+
@RequestParam(value = "nic", required = false) String nic,
119+
@RequestParam(value = "photo", required = false) MultipartFile photo,
120+
@RequestParam(value = "risk_level", required = false) String riskLevel,
121+
@RequestParam(value = "crime_history", required = false) String crimeHistory,
122+
@RequestParam(value = "address", required = false) String address,
123+
@RequestParam(value = "contact_number", required = false) String contactNumber,
124+
@RequestParam(value = "secondary_contact", required = false) String secondaryContact,
125+
@RequestParam(value = "date_of_birth", required = false) String dateOfBirth,
126+
@RequestParam(value = "gender", required = false) String gender,
127+
@RequestParam(value = "alias", required = false) String alias,
128+
@RequestParam(value = "status", required = false) String status) {
129+
130+
try {
131+
log.info("Criminal update requested for ID: {}", LogSanitizer.sanitize(criminalId));
132+
133+
Optional<Map<String, Object>> result = criminalService.updateCriminal(
134+
criminalId, name, nic, riskLevel, crimeHistory,
135+
address, contactNumber, secondaryContact,
136+
dateOfBirth, gender, alias, status, photo);
137+
138+
if (result.isEmpty()) {
139+
return ResponseEntity.status(404)
140+
.body(Map.of("error", "Criminal not found: " + LogSanitizer.sanitize(criminalId)));
141+
}
142+
143+
Map<String, Object> response = new java.util.LinkedHashMap<>();
144+
response.put("message", "Criminal updated successfully");
145+
response.put("criminal", result.get());
146+
return ResponseEntity.ok(response);
147+
148+
} catch (Exception e) {
149+
log.error("Criminal update failed for {}: {}", LogSanitizer.sanitize(criminalId), e.getMessage());
150+
return ResponseEntity.internalServerError()
151+
.body(Map.of("error", "Update failed: " + e.getMessage()));
152+
}
153+
}
154+
155+
/**
156+
* Delete a criminal record (cascades to suspect_photos, cleans up storage).
157+
*/
158+
@DeleteMapping("/{criminalId}")
159+
public ResponseEntity<?> deleteCriminal(@PathVariable String criminalId) {
160+
try {
161+
log.info("Criminal deletion requested for ID: {}", LogSanitizer.sanitize(criminalId));
162+
163+
boolean deleted = criminalService.deleteCriminal(criminalId);
164+
if (!deleted) {
165+
return ResponseEntity.status(404)
166+
.body(Map.of("error", "Criminal not found: " + LogSanitizer.sanitize(criminalId)));
167+
}
168+
169+
return ResponseEntity.ok(Map.of("message", "Criminal deleted successfully", "criminal_id", criminalId));
170+
171+
} catch (Exception e) {
172+
log.error("Criminal deletion failed for {}: {}", LogSanitizer.sanitize(criminalId), e.getMessage());
173+
return ResponseEntity.internalServerError()
174+
.body(Map.of("error", "Deletion failed: " + e.getMessage()));
175+
}
176+
}
177+
}

src/main/java/com/crimeLink/analyzer/controller/FacialRecognitionController.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,15 @@ public ResponseEntity<?> registerCriminal(
9898
@RequestParam(value = "criminal_id", required = false) String criminalId,
9999
@RequestParam("name") String name,
100100
@RequestParam("nic") String nic,
101-
@RequestParam(value = "risk_level", required = false, defaultValue = "medium") String riskLevel) {
101+
@RequestParam(value = "risk_level", required = false, defaultValue = "medium") String riskLevel,
102+
@RequestParam(value = "crime_history", required = false) String crimeHistory,
103+
@RequestParam(value = "address", required = false) String address,
104+
@RequestParam(value = "contact_number", required = false) String contactNumber,
105+
@RequestParam(value = "secondary_contact", required = false) String secondaryContact,
106+
@RequestParam(value = "date_of_birth", required = false) String dateOfBirth,
107+
@RequestParam(value = "gender", required = false) String gender,
108+
@RequestParam(value = "alias", required = false) String alias,
109+
@RequestParam(value = "status", required = false, defaultValue = "active") String status) {
102110

103111
try {
104112
log.info("Criminal registration requested: {} ({})", LogSanitizer.sanitize(name), LogSanitizer.sanitize(nic));
@@ -122,7 +130,8 @@ public ResponseEntity<?> registerCriminal(
122130

123131
// Forward to ML service
124132
JsonNode result = facialRecognitionService.registerCriminal(
125-
photo, criminalId, name, nic, riskLevel);
133+
photo, criminalId, name, nic, riskLevel, crimeHistory,
134+
address, contactNumber, secondaryContact, dateOfBirth, gender, alias, status);
126135

127136
return ResponseEntity.ok(result);
128137

src/main/java/com/crimeLink/analyzer/controller/UserController.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
package com.crimeLink.analyzer.controller;
22

3-
43
import com.crimeLink.analyzer.entity.User;
54
import com.crimeLink.analyzer.service.UserService;
6-
import com.crimeLink.analyzer.service.WeaponIssueService;
7-
import org.springframework.beans.factory.annotation.Autowired;
85
import org.springframework.web.bind.annotation.*;
96

107
import java.util.List;
@@ -19,11 +16,13 @@ public UserController(UserService service) {
1916
this.service = service;
2017
}
2118

22-
@GetMapping("/field-officers")
2319
public List<User> getFieldOfficers() {
2420
return service.getFieldOfficers();
2521
}
2622

23+
@GetMapping("/all-officers")
24+
public List<User> getAllOfficers() {
25+
return service.getAllOfficers();
26+
}
2727

2828
}
29-
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.crimeLink.analyzer.dto;
2+
3+
import lombok.Data;
4+
5+
@Data
6+
public class BulletAddDTO {
7+
private String bulletType;
8+
private Integer numberOfMagazines;
9+
private String remarks;
10+
}

0 commit comments

Comments
 (0)