package com.dalab.autocompliance.controller; import com.dalab.autocompliance.dto.ComplianceReportDefinitionDTO; import com.dalab.autocompliance.dto.ReportGenerationRequestDTO; import com.dalab.autocompliance.dto.ReportGenerationResponseDTO; import com.dalab.autocompliance.dto.ReportJobStatusDTO; import com.dalab.autocompliance.dto.ComplianceReportDTO; import com.dalab.autocompliance.dto.AssetComplianceStatusDTO; import com.dalab.autocompliance.dto.ComplianceControlDTO; import com.dalab.autocompliance.dto.ControlEvaluationRequestDTO; import com.dalab.autocompliance.dto.ControlEvaluationResponseDTO; import com.dalab.autocompliance.service.IComplianceService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/v1/compliance") @Tag(name = "Compliance API", description = "APIs for managing compliance checks, reports, and evaluations") @RequiredArgsConstructor public class ComplianceController { private final IComplianceService complianceService; @Operation(summary = "List available compliance report definitions", description = "Provides a list of all configured compliance report types that can be generated.", responses = { @ApiResponse(responseCode = "200", description = "Successfully retrieved report definitions") }) @GetMapping("/reports") @PreAuthorize("hasAnyRole('ADMIN', 'DATA_STEWARD', 'USER', 'AUDITOR')") // Broad access to see what reports are available public ResponseEntity> listAvailableReportDefinitions() { List definitions = complianceService.listAvailableReportDefinitions(); return ResponseEntity.ok(definitions); } @Operation(summary = "Generate a compliance report", description = "Triggers the asynchronous generation of a specific compliance report based on its type and provided parameters.", responses = { @ApiResponse(responseCode = "202", description = "Report generation request accepted."), @ApiResponse(responseCode = "400", description = "Invalid request (e.g., missing parameters, report type not found)"), @ApiResponse(responseCode = "404", description = "Report type not found") // Covered by 400 in current impl }) @PostMapping("/reports/{reportType}/generate") @PreAuthorize("hasAnyRole('ADMIN', 'DATA_STEWARD')") // Users who can trigger generation public ResponseEntity generateComplianceReport( @Parameter(description = "Unique identifier of the report type to generate", required = true) @PathVariable String reportType, @Parameter(description = "Parameters required for the report generation", required = true) @Valid @RequestBody ReportGenerationRequestDTO request) { ReportGenerationResponseDTO response = complianceService.generateComplianceReport(reportType, request); if ("FAILED_VALIDATION".equals(response.getStatus())) { return ResponseEntity.badRequest().body(response); } return ResponseEntity.status(HttpStatus.ACCEPTED).body(response); } @Operation(summary = "Get compliance report generation job status", description = "Retrieves the current status of an asynchronous report generation job.", responses = { @ApiResponse(responseCode = "200", description = "Successfully retrieved job status."), @ApiResponse(responseCode = "404", description = "Job ID not found.") }) @GetMapping("/reports/jobs/{jobId}") @PreAuthorize("hasAnyRole('ADMIN', 'DATA_STEWARD', 'USER', 'AUDITOR')") // Users who can view job status public ResponseEntity getReportGenerationJobStatus( @Parameter(description = "ID of the report generation job", required = true) @PathVariable String jobId) { ReportJobStatusDTO jobStatus = complianceService.getReportGenerationJobStatus(jobId); if ("NOT_FOUND".equals(jobStatus.getStatus())) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body(jobStatus); } return ResponseEntity.ok(jobStatus); } @Operation(summary = "Retrieve a generated compliance report", description = "Fetches the detailed contents of a previously generated compliance report.", responses = { @ApiResponse(responseCode = "200", description = "Successfully retrieved the report."), @ApiResponse(responseCode = "404", description = "Report ID not found.") }) @GetMapping("/reports/results/{reportId}") @PreAuthorize("hasAnyRole('ADMIN', 'DATA_STEWARD', 'USER', 'AUDITOR')") // Users who can view reports public ResponseEntity getGeneratedReport( @Parameter(description = "ID of the generated compliance report", required = true) @PathVariable String reportId) { ComplianceReportDTO report = complianceService.getGeneratedReport(reportId); if (report == null) { return ResponseEntity.notFound().build(); } return ResponseEntity.ok(report); } @Operation(summary = "Get compliance status for an asset", description = "Retrieves the overall compliance status and recent findings for a specific asset.", responses = { @ApiResponse(responseCode = "200", description = "Successfully retrieved asset compliance status."), @ApiResponse(responseCode = "404", description = "Asset ID not found or no compliance data available.") }) @GetMapping("/assets/{assetId}/status") @PreAuthorize("hasAnyRole('ADMIN', 'DATA_STEWARD', 'USER', 'AUDITOR')") public ResponseEntity getAssetComplianceStatus( @Parameter(description = "ID of the asset (e.g., cloud resource ID)", required = true) @PathVariable String assetId) { AssetComplianceStatusDTO status = complianceService.getAssetComplianceStatus(assetId); if ("UNKNOWN".equals(status.getOverallComplianceStatus()) && status.getRelevantReportIds() == null) { // Basic check for 'not found' return ResponseEntity.status(HttpStatus.NOT_FOUND).body(status); // Provide status with UNKNOWN if created that way } return ResponseEntity.ok(status); } @Operation(summary = "List available compliance controls", description = "Provides a list of all configured and typically enabled compliance controls that can be evaluated.", responses = { @ApiResponse(responseCode = "200", description = "Successfully retrieved compliance controls") }) @GetMapping("/controls") @PreAuthorize("hasAnyRole('ADMIN', 'DATA_STEWARD', 'USER', 'AUDITOR')") // Broad access to see available controls public ResponseEntity> listAvailableControls() { List controls = complianceService.listAvailableControls(); return ResponseEntity.ok(controls); } @Operation(summary = "Evaluate a compliance control", description = "Triggers the asynchronous evaluation of a specific compliance control.", responses = { @ApiResponse(responseCode = "202", description = "Control evaluation request accepted."), @ApiResponse(responseCode = "400", description = "Invalid request (e.g., missing parameters, control not enabled)"), @ApiResponse(responseCode = "404", description = "Control ID not found") }) @PostMapping("/controls/{controlId}/evaluate") @PreAuthorize("hasAnyRole('ADMIN', 'DATA_STEWARD')") // Users who can trigger evaluations public ResponseEntity evaluateControl( @Parameter(description = "Unique identifier of the control to evaluate", required = true) @PathVariable String controlId, @Parameter(description = "Parameters for the control evaluation", required = true) @Valid @RequestBody ControlEvaluationRequestDTO request) { ControlEvaluationResponseDTO response = complianceService.evaluateControl(controlId, request); if ("FAILED_VALIDATION".equals(response.getStatus())) { return ResponseEntity.badRequest().body(response); } // Assuming ControlNotFoundException is handled by a global exception handler to return 404 return ResponseEntity.status(HttpStatus.ACCEPTED).body(response); } // TODO: Add endpoint for GET /controls/evaluations/{jobId} (status of a control evaluation job) }