Skip to content

Commit

Permalink
[backend] Change external reference resolution for OpenCTI integration (
Browse files Browse the repository at this point in the history
  • Loading branch information
RomuDeuxfois authored and johanah29 committed Jun 7, 2024
1 parent af85fe3 commit 4d9272e
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 59 deletions.
43 changes: 43 additions & 0 deletions openbas-api/src/main/java/io/openbas/opencti/OpenCTIApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.openbas.opencti;

import io.openbas.database.model.Exercise;
import io.openbas.rest.exercise.form.ExerciseSimple;
import io.openbas.service.ScenarioService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import static io.openbas.database.model.User.ROLE_USER;

@RequiredArgsConstructor
@RestController
@Secured(ROLE_USER)
public class OpenCTIApi {

public static final String OPENCTI_URI = "/api/opencti/v1";

private final ScenarioService scenarioService;

@Operation(summary = "Retrieve the latest exercise by external reference ID (example: a report ID")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Found the exercise",
content = {
@Content(mediaType = "application/json", schema = @Schema(implementation = ExerciseSimple.class))
}),
@ApiResponse(responseCode = "404", description = "Exercise not found", content = @Content)
})
@GetMapping(OPENCTI_URI + "/exercises/latest/{externalReferenceId}")
public ExerciseSimple latestExerciseByExternalReference(@PathVariable @NotBlank final String externalReferenceId) {
Exercise exercise = this.scenarioService.latestExerciseByExternalReference(externalReferenceId);
return ExerciseSimple.fromExercise(exercise);
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package io.openbas.rest.scenario;

import io.openbas.database.model.Scenario;
import io.openbas.database.model.Team;
import io.openbas.database.model.TeamSimple;
import io.openbas.database.model.User;
import io.openbas.database.model.*;
import io.openbas.database.raw.RawPaginationScenario;
import io.openbas.database.repository.*;
import io.openbas.rest.exception.ElementNotFoundException;
Expand Down Expand Up @@ -108,11 +105,6 @@ public Scenario scenario(@PathVariable @NotBlank final String scenarioId) {
return scenarioService.scenario(scenarioId);
}

@GetMapping(SCENARIO_URI + "/external_reference/{externalReferenceId}")
public Scenario scenarioByExternalId(@PathVariable @NotBlank final String externalReferenceId) {
return scenarioService.scenarioByExternalReference(externalReferenceId);
}

@PutMapping(SCENARIO_URI + "/{scenarioId}")
@PreAuthorize("isScenarioPlanner(#scenarioId)")
public Scenario updateScenario(
Expand Down Expand Up @@ -176,16 +168,13 @@ public void importScenario(@RequestPart("file") @NotNull MultipartFile file) thr

// -- SIMULATION --

// region scenarios
@GetMapping(SCENARIO_URI + "/{scenarioId}/exercises")
@PreAuthorize("isScenarioObserver(#scenarioId)")
public Iterable<ExerciseSimple> scenarioExercises(@PathVariable @NotBlank final String scenarioId) {
Scenario scenario = this.scenarioService.scenario(scenarioId);
return scenario.getExercises().stream().map(ExerciseSimple::fromExercise).toList();
}

// endregion

// -- TEAMS --

@Transactional(rollbackOn = Exception.class)
Expand Down
18 changes: 10 additions & 8 deletions openbas-api/src/main/java/io/openbas/service/ScenarioService.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.*;
import java.util.logging.Level;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
Expand Down Expand Up @@ -227,12 +224,17 @@ public Scenario scenario(@NotBlank final String scenarioId) {
.orElseThrow(() -> new ElementNotFoundException("Scenario not found"));
}

public Scenario scenarioByExternalReference(@NotBlank final String scenarioExternalReference) {
@Transactional(readOnly = true)
public Exercise latestExerciseByExternalReference(@NotBlank final String scenarioExternalReference) {
List<Scenario> scenarios = this.scenarioRepository.findByExternalReference(scenarioExternalReference);
if (!scenarios.isEmpty()) {
return scenarios.getFirst();
Optional<Exercise> latestEndedExercise = scenarios.stream()
.flatMap(scenario -> scenario.getExercises().stream())
.filter(exercise -> exercise.getEnd().isPresent())
.max(Comparator.comparing(exercise -> exercise.getEnd().get()));
if (latestEndedExercise.isPresent()) {
return latestEndedExercise.get();
} else {
throw new ElementNotFoundException("Scenario not found");
throw new ElementNotFoundException("Latest exercise not found");
}
}

Expand Down
82 changes: 44 additions & 38 deletions openbas-api/src/main/java/io/openbas/utils/ResultUtils.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package io.openbas.utils;

import io.openbas.utils.AtomicTestingMapper.ExpectationResultsByType;
import io.openbas.database.model.AttackPattern;
import io.openbas.database.model.Inject;
import io.openbas.database.model.InjectExpectation;
import io.openbas.rest.atomic_testing.form.InjectTargetWithResult;
import io.openbas.database.model.*;
import io.openbas.rest.inject.form.InjectExpectationResultsByAttackPattern;
import io.openbas.utils.AtomicTestingMapper.ExpectationResultsByType;
import jakarta.validation.constraints.NotNull;

import java.util.List;
Expand All @@ -14,41 +16,45 @@

public class ResultUtils {

// -- GLOBAL SCORE --

public static List<ExpectationResultsByType> computeGlobalExpectationResults(@NotNull final List<Inject> injects) {
List<InjectExpectation> expectations = injects
.stream()
.flatMap((inject) -> inject.getExpectations().stream())
.toList();
return AtomicTestingUtils.getExpectationResultByTypes(expectations);
}

public static List<InjectExpectationResultsByAttackPattern> computeInjectExpectationResults(@NotNull final List<Inject> injects) {
Map<AttackPattern, List<Inject>> groupedByAttackPattern = injects.stream()
.flatMap((inject) -> inject.getInjectorContract()
.getAttackPatterns()
.stream()
.map(attackPattern -> Map.entry(attackPattern, inject))
)
.collect(Collectors.groupingBy(
java.util.Map.Entry::getKey,
Collectors.mapping(java.util.Map.Entry::getValue, Collectors.toList())
));

return groupedByAttackPattern.entrySet()
.stream()
.map(entry -> new InjectExpectationResultsByAttackPattern(entry.getKey(), entry.getValue()))
.toList();
}

// -- TARGET --

public static List<InjectTargetWithResult> computeTargetResults(@NotNull final List<Inject> injects) {
return injects.stream()
.flatMap((inject) -> getTargetsWithResults(inject).stream())
.distinct()
.toList();
}
private ResultUtils() {
}

// -- GLOBAL SCORE --

public static List<ExpectationResultsByType> computeGlobalExpectationResults(@NotNull final List<Inject> injects) {
List<InjectExpectation> expectations = injects
.stream()
.flatMap(inject -> inject.getExpectations().stream())
.toList();
return AtomicTestingUtils.getExpectationResultByTypes(expectations);
}

public static List<InjectExpectationResultsByAttackPattern> computeInjectExpectationResults(
@NotNull final List<Inject> injects) {
Map<AttackPattern, List<Inject>> groupedByAttackPattern = injects.stream()
.flatMap(inject -> inject.getInjectorContract()
.getAttackPatterns()
.stream()
.map(attackPattern -> Map.entry(attackPattern, inject))
)
.collect(Collectors.groupingBy(
java.util.Map.Entry::getKey,
Collectors.mapping(java.util.Map.Entry::getValue, Collectors.toList())
));

return groupedByAttackPattern.entrySet()
.stream()
.map(entry -> new InjectExpectationResultsByAttackPattern(entry.getKey(), entry.getValue()))
.toList();
}

// -- TARGET --

public static List<InjectTargetWithResult> computeTargetResults(@NotNull final List<Inject> injects) {
return injects.stream()
.flatMap(inject -> getTargetsWithResults(inject).stream())
.distinct()
.toList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public interface ScenarioRepository extends CrudRepository<Scenario, String>,
JpaSpecificationExecutor<Scenario> {

@NotNull
List<Scenario> findByExternalReference(@Param("externalReference") String externalReference);
List<Scenario> findByExternalReference(@Param("externalReference") final String externalReference);

@Query("select distinct s from Scenario s " +
"join s.grants as grant " +
Expand Down

0 comments on commit 4d9272e

Please sign in to comment.