Skip to content

Commit 610cfb5

Browse files
majaberejberejmaj
andauthored
api key verification for threat intel plus deleteing coderpo (#72)
Co-authored-by: berejmaj <maja.berej@orange.com>
1 parent 5a168d2 commit 610cfb5

File tree

12 files changed

+173
-2
lines changed

12 files changed

+173
-2
lines changed

backend/src/main/java/io/mixeway/mixewayflowapi/api/coderepo/controller/CodeRepoController.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.mixeway.mixewayflowapi.api.coderepo.service.CodeRepoApiService;
55
import io.mixeway.mixewayflowapi.db.entity.CodeRepo;
66
import io.mixeway.mixewayflowapi.domain.coderepo.CreateCodeRepoService;
7+
import io.mixeway.mixewayflowapi.domain.coderepo.DeleteCodeRepoService;
78
import io.mixeway.mixewayflowapi.exceptions.CodeRepoNotFoundException;
89
import io.mixeway.mixewayflowapi.exceptions.TeamNotFoundException;
910
import io.mixeway.mixewayflowapi.exceptions.UnauthorizedException;
@@ -27,6 +28,7 @@
2728
public class CodeRepoController {
2829
private final CreateCodeRepoService createCodeRepoService;
2930
private final CodeRepoApiService codeRepoApiService;
31+
private final DeleteCodeRepoService deleteCodeRepoService;
3032

3133
@PreAuthorize("hasAuthority('USER')")
3234
@PostMapping(value= "/api/v1/coderepo/create/gitlab")
@@ -160,4 +162,22 @@ public ResponseEntity<StatusDTO> renameCodeRepo(
160162
return new ResponseEntity<>(new StatusDTO(e.getMessage()), HttpStatus.BAD_REQUEST);
161163
}
162164
}
165+
166+
@PreAuthorize("hasAuthority('ADMIN')")
167+
@DeleteMapping(value = "/api/v1/coderepo/{id}")
168+
public ResponseEntity<StatusDTO> deleteCodeRepo(@PathVariable("id") Long id, Principal principal) {
169+
try {
170+
deleteCodeRepoService.deleteRepo(id, principal);
171+
return ResponseEntity.ok(new StatusDTO("Repository deleted."));
172+
} catch (UnauthorizedException e) {
173+
log.error("[CodeRepo] Unauthorized delete attempt for {} by {}", id, principal.getName());
174+
return new ResponseEntity<>(new StatusDTO("Unauthorized"), HttpStatus.FORBIDDEN);
175+
} catch (CodeRepoNotFoundException e) {
176+
log.error("[CodeRepo] Repository not found for delete {} by {}", id, principal.getName());
177+
return new ResponseEntity<>(new StatusDTO(e.getMessage()), HttpStatus.NOT_FOUND);
178+
} catch (Exception e) {
179+
log.error("[CodeRepo] Delete failed for id {} by {}: {}", id, principal.getName(), e.getMessage());
180+
return new ResponseEntity<>(new StatusDTO("Internal server error"), HttpStatus.INTERNAL_SERVER_ERROR);
181+
}
182+
}
163183
}

backend/src/main/java/io/mixeway/mixewayflowapi/api/threatintel/controller/ThreatIntelController.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.mixeway.mixewayflowapi.api.threatintel.controller;
22

3+
import io.mixeway.mixewayflowapi.api.teamfindings.controller.FindingsByTeamController;
4+
import io.mixeway.mixewayflowapi.api.teamfindings.service.FindingsByTeamService;
35
import io.mixeway.mixewayflowapi.api.threatintel.dto.ItemListResponse;
46
import io.mixeway.mixewayflowapi.api.threatintel.dto.RemovedVulnerabilityDTO;
57
import io.mixeway.mixewayflowapi.api.threatintel.dto.ReviewedVulnerabilityDTO;
@@ -15,6 +17,7 @@
1517
import org.springframework.validation.annotation.Validated;
1618
import org.springframework.web.bind.annotation.GetMapping;
1719
import org.springframework.web.bind.annotation.PathVariable;
20+
import org.springframework.web.bind.annotation.RequestHeader;
1821
import org.springframework.web.bind.annotation.RestController;
1922

2023
import java.security.Principal;
@@ -27,6 +30,7 @@
2730
public class ThreatIntelController {
2831

2932
private final ThreatIntelService threatIntelService;
33+
private final FindingsByTeamService findingsByTeamService;
3034

3135
@PreAuthorize("hasAuthority('USER')")
3236
@GetMapping(value= "/api/v1/threat-intel/findings")
@@ -36,8 +40,11 @@ public ResponseEntity<ItemListResponse> getThreats(Principal principal){
3640

3741
@PreAuthorize("hasAuthority('USER')")
3842
@GetMapping(value= "/api/v1/threat-intel/findings/{remoteId}")
39-
public ResponseEntity<ItemListResponse> getThreatsForTeam(Principal principal, @PathVariable("remoteId") String remoteId){
43+
public ResponseEntity<ItemListResponse> getThreatsForTeam(@RequestHeader("X-API-KEY") String apiKey, Principal principal, @PathVariable("remoteId") String remoteId){
4044
try {
45+
if (!findingsByTeamService.isValidApiKey(apiKey)) {
46+
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
47+
}
4148
return threatIntelService.getThreatsForTeam(principal, remoteId);
4249
} catch (TeamNotFoundException e){
4350
log.warn(e.getLocalizedMessage());

backend/src/main/java/io/mixeway/mixewayflowapi/db/entity/CodeRepo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,4 +264,4 @@ public boolean equals(Object o) {
264264
public int hashCode() {
265265
return Objects.hash(name);
266266
}
267-
}
267+
}

backend/src/main/java/io/mixeway/mixewayflowapi/db/repository/CodeRepoFindingStatsRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,6 @@ List<DailyFindings> findETAFindingsBetweenDates(@Param("startDate") LocalDate st
8383
*/
8484
List<CodeRepoFindingStats> findByCodeRepoOrderByDateInsertedDesc(CodeRepo codeRepo);
8585

86+
void deleteByCodeRepo(CodeRepo codeRepo);
87+
8688
}

backend/src/main/java/io/mixeway/mixewayflowapi/db/repository/FindingRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ Finding findFirstByCodeRepoAndVulnerabilityAndLocationAndStatus(CodeRepo codeRep
140140
String location,
141141
Finding.Status status);
142142

143+
void deleteByCodeRepo(CodeRepo codeRepo);
144+
143145
@Modifying(clearAutomatically = true, flushAutomatically = true)
144146
@Query("""
145147
update Finding f

backend/src/main/java/io/mixeway/mixewayflowapi/db/repository/ScanInfoRepository.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ public interface ScanInfoRepository extends CrudRepository<ScanInfo, Long> {
1414

1515
Optional<ScanInfo> findByCodeRepoAndCodeRepoBranchAndCommitId(CodeRepo codeRepo, CodeRepoBranch codeRepoBranch, String commitId);
1616
List<ScanInfo> findByCodeRepo(CodeRepo repo);
17+
void deleteByCodeRepo(CodeRepo codeRepo);
1718
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package io.mixeway.mixewayflowapi.domain.coderepo;
2+
3+
import io.mixeway.mixewayflowapi.db.entity.CodeRepo;
4+
import io.mixeway.mixewayflowapi.db.repository.AppDataTypeRepository;
5+
import io.mixeway.mixewayflowapi.db.repository.CodeRepoFindingStatsRepository;
6+
import io.mixeway.mixewayflowapi.db.repository.CodeRepoRepository;
7+
import io.mixeway.mixewayflowapi.db.repository.FindingRepository;
8+
import io.mixeway.mixewayflowapi.db.repository.ScanInfoRepository;
9+
import io.mixeway.mixewayflowapi.exceptions.CodeRepoNotFoundException;
10+
import io.mixeway.mixewayflowapi.utils.PermissionFactory;
11+
import lombok.RequiredArgsConstructor;
12+
import lombok.extern.log4j.Log4j2;
13+
import org.springframework.stereotype.Service;
14+
import org.springframework.transaction.annotation.Transactional;
15+
16+
import java.security.Principal;
17+
import java.util.Collections;
18+
19+
@Service
20+
@RequiredArgsConstructor
21+
@Log4j2
22+
public class DeleteCodeRepoService {
23+
private final FindCodeRepoService findCodeRepoService;
24+
private final CodeRepoRepository codeRepoRepository;
25+
private final FindingRepository findingRepository;
26+
private final ScanInfoRepository scanInfoRepository;
27+
private final CodeRepoFindingStatsRepository codeRepoFindingStatsRepository;
28+
private final AppDataTypeRepository appDataTypeRepository;
29+
private final PermissionFactory permissionFactory;
30+
31+
@Transactional
32+
public void deleteRepo(Long repoId, Principal principal) {
33+
CodeRepo repo = findCodeRepoService.findById(repoId)
34+
.orElseThrow(() -> new CodeRepoNotFoundException("Code repository not found"));
35+
36+
permissionFactory.canUserManageTeam(repo.getTeam(), principal);
37+
38+
if (repo.getComponents() != null && !repo.getComponents().isEmpty()) {
39+
repo.setComponents(Collections.emptyList());
40+
}
41+
42+
findingRepository.deleteByCodeRepo(repo);
43+
scanInfoRepository.deleteByCodeRepo(repo);
44+
codeRepoFindingStatsRepository.deleteByCodeRepo(repo);
45+
appDataTypeRepository.deleteAllByCodeRepo(repo);
46+
codeRepoRepository.delete(repo);
47+
48+
log.info("[CodeRepo] Deleted repository {} by {}", repoId, principal.getName());
49+
}
50+
}

frontend/src/app/service/RepoService.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,10 @@ export class RepoService {
6969
{ withCredentials: true }
7070
);
7171
}
72+
deleteRepo(repoId: number): Observable<any> {
73+
return this.http.delete<any>(
74+
`${this.loginUrl}/api/v1/coderepo/${repoId}`,
75+
{ withCredentials: true }
76+
);
77+
}
7278
}

frontend/src/app/views/show-repo/repository-info/repository-info.component.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ <h2 class="mb-0 repo-title">
4848
<svg cIcon name="cil-pencil" class="me-1"></svg>
4949
Rename
5050
</button>
51+
<button *ngIf="userRole === 'ADMIN'"
52+
cButton color="danger" variant="ghost" size="sm"
53+
[cTooltip]="'Delete repository'"
54+
(click)="requestDeleteRepo()">
55+
<svg cIcon name="cil-trash" class="me-1"></svg>
56+
Delete
57+
</button>
5158
</div>
5259
</c-card-header>
5360

frontend/src/app/views/show-repo/repository-info/repository-info.component.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export class RepositoryInfoComponent implements OnInit {
8686

8787
@Output() runScanEvent = new EventEmitter<void>();
8888
@Output() openChangeTeamModalEvent = new EventEmitter<void>();
89+
@Output() deleteRepoEvent = new EventEmitter<void>();
8990

9091
ngOnInit(): void {
9192
// Enhance chart options with better defaults
@@ -124,6 +125,9 @@ export class RepositoryInfoComponent implements OnInit {
124125
this.renameForm.name = this.repoData?.name ?? '';
125126
this.renameModalVisible = true;
126127
}
128+
requestDeleteRepo(): void {
129+
this.deleteRepoEvent.emit();
130+
}
127131
confirmRename() {
128132
const id = this.repoData?.id;
129133
if (!id) return;

0 commit comments

Comments
 (0)