Skip to content

Commit

Permalink
Merge pull request #2263 from opengovern/fix-control-summary
Browse files Browse the repository at this point in the history
fix: fix get audit job
  • Loading branch information
artaasadi authored Dec 17, 2024
2 parents 9b8b59e + c1fa50a commit f435bc5
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 87 deletions.
40 changes: 40 additions & 0 deletions services/compliance/api/audit_summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
"github.com/opengovern/opencomply/pkg/types"
"time"
)

type AuditSummary struct {
Expand All @@ -10,3 +11,42 @@ type AuditSummary struct {
AuditSummary map[types.ComplianceStatus]uint64 `json:"audit_summary"`
JobSummary types.JobSummary `json:"job_summary"`
}

type GetJobReportSummaryJobDetails struct {
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Framework struct {
ID string `json:"id"`
Title string `json:"title"`
} `json:"framework"`
IntegrationIDs []string `json:"integration_ids"`
JobScore JobScore `json:"job_score"`
Results GetJobReportSummaryJobDetailsResults `json:"results"`
}

type GetJobReportSummaryJobDetailsResults struct {
Alarm uint64 `json:"alarm"`
Ok uint64 `json:"ok"`
}

type JobScore struct {
TotalControls int64 `json:"total_controls"`
FailedControls int64 `json:"failed_controls"`
ControlView JobScoreControlView `json:"control_view"`
}

type JobScoreControlView struct {
BySeverity map[string]*JobScoreControlViewBySeverityScore `json:"by_severity"`
}

type JobScoreControlViewBySeverityScore struct {
TotalControls uint64 `json:"total_controls"`
FailedControls uint64 `json:"failed_controls"`
}

type GetJobReportSummaryResponse struct {
JobID uint `json:"job_id"`
WithIncidents bool `json:"with_incidents"`
JobDetails GetJobReportSummaryJobDetails `json:"job_details"`
}
201 changes: 150 additions & 51 deletions services/compliance/http_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ func (h *HttpHandler) Register(e *echo.Echo) {
v3.GET("/quick/scan/:run_id", httpserver2.AuthorizeHandler(h.GetQuickScanSummary, authApi.ViewerRole))
v3.GET("/quick/sequence/:run_id", httpserver2.AuthorizeHandler(h.GetQuickSequenceSummary, authApi.ViewerRole))

v3.GET("/compliance/report/:run_id", httpserver2.AuthorizeHandler(h.GetComplianceJobReport, authApi.ViewerRole))
v3.GET("/job-report/:run_id/details/by-control", httpserver2.AuthorizeHandler(h.GetComplianceJobReport, authApi.ViewerRole))
v3.GET("/job-report/:run_id/summary", httpserver2.AuthorizeHandler(h.GetJobReportSummary, authApi.ViewerRole))
}

func bindValidate(ctx echo.Context, i any) error {
Expand Down Expand Up @@ -7665,45 +7666,38 @@ func (h HttpHandler) GetQuickScanSummary(c echo.Context) error {

jobId := c.Param("run_id")
view := c.QueryParam("view")
withIncidentsStr := c.QueryParam("with_incidents")
controls := httpserver2.QueryArrayParam(c, "controls")

if view == "" {
view = "control"
}

withIncidents := false
if withIncidentsStr == "true" {
withIncidents = true
complianceJob, err := h.schedulerClient.GetComplianceJobStatus(clientCtx, jobId)
if err != nil {
h.logger.Error("failed to get compliance job", zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get compliance job")
}
complianceJob, err := h.schedulerClient.GetComplianceJobStatus(clientCtx, jobId)
if err != nil {
h.logger.Error("failed to get compliance job", zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get compliance job")
}
if complianceJob.JobStatus == string(schedulerapi.ComplianceJobFailed) {
return echo.NewHTTPError(http.StatusBadRequest, "job has been failed")
} else if complianceJob.JobStatus == string(schedulerapi.ComplianceJobTimeout) {
return echo.NewHTTPError(http.StatusBadRequest, "job has been timed out")
} else if complianceJob.JobStatus == string(schedulerapi.ComplianceJobRunnersInProgress) ||
complianceJob.JobStatus == string(schedulerapi.ComplianceJobCreated) ||
complianceJob.JobStatus == string(schedulerapi.ComplianceJobSummarizerInProgress) {
return echo.NewHTTPError(http.StatusBadRequest, "job is in progress")
}
if complianceJob.WithIncidents {
if complianceJob.SummaryJobId == nil {
return echo.NewHTTPError(http.StatusBadRequest, "compliance job not summarized yet")
}
jobId = strconv.Itoa(int(*complianceJob.SummaryJobId))
} else {
auditJob, err := h.schedulerClient.GetComplianceQuickRun(clientCtx, jobId)
if err != nil {
h.logger.Error("failed to get audit job", zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get audit job")
}
if auditJob.Status == schedulerapi.ComplianceJobFailed {
return echo.NewHTTPError(http.StatusBadRequest, "job has been failed")
} else if auditJob.Status == schedulerapi.ComplianceJobTimeout {
return echo.NewHTTPError(http.StatusBadRequest, "job has been timed out")
} else if auditJob.Status == schedulerapi.ComplianceJobRunnersInProgress || auditJob.Status == schedulerapi.ComplianceJobCreated || auditJob.Status == schedulerapi.ComplianceJobSummarizerInProgress {
return echo.NewHTTPError(http.StatusBadRequest, "job is in progress")
}
}

var result api.AuditSummary

switch view {
case "resource", "resources":
summary, err := es.GetJobReportResourceViewByJobID(c.Request().Context(), h.logger, h.client, jobId, withIncidents)
summary, err := es.GetJobReportResourceViewByJobID(c.Request().Context(), h.logger, h.client, jobId, complianceJob.WithIncidents)
if err != nil {
h.logger.Error("failed to get audit job summary", zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get audit job summary")
Expand All @@ -7717,7 +7711,7 @@ func (h HttpHandler) GetQuickScanSummary(c echo.Context) error {
JobSummary: summary.JobSummary,
}
case "control", "controls":
summary, err := es.GetJobReportControlViewByJobID(c.Request().Context(), h.logger, h.client, jobId, withIncidents, controls)
summary, err := es.GetJobReportControlViewByJobID(c.Request().Context(), h.logger, h.client, jobId, complianceJob.WithIncidents, controls)
if err != nil {
h.logger.Error("failed to get audit job summary", zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get audit job summary")
Expand All @@ -7731,12 +7725,12 @@ func (h HttpHandler) GetQuickScanSummary(c echo.Context) error {
JobSummary: summary.JobSummary,
}
case "both":
controlSummary, err := es.GetJobReportControlViewByJobID(c.Request().Context(), h.logger, h.client, jobId, withIncidents, controls)
controlSummary, err := es.GetJobReportControlViewByJobID(c.Request().Context(), h.logger, h.client, jobId, complianceJob.WithIncidents, controls)
if err != nil {
h.logger.Error("failed to get audit job summary", zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get audit job summary")
}
resourceSummary, err := es.GetJobReportResourceViewByJobID(c.Request().Context(), h.logger, h.client, jobId, withIncidents)
resourceSummary, err := es.GetJobReportResourceViewByJobID(c.Request().Context(), h.logger, h.client, jobId, complianceJob.WithIncidents)
if err != nil {
h.logger.Error("failed to get audit job summary", zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get audit job summary")
Expand Down Expand Up @@ -7862,47 +7856,152 @@ func (h HttpHandler) GetQuickSequenceSummary(c echo.Context) error {
// @Param with_incidents query bool false "Whether the job was with incidents or not"
// @Param run_id path string true "compliance summary job id"
// @Success 200
// @Router /compliance/api/v3/compliance/report/{run_id} [get]
// @Router /compliance/api/v3/job-report/:run_id/details/by-control [get]
func (h HttpHandler) GetComplianceJobReport(c echo.Context) error {
clientCtx := &httpclient.Context{UserRole: authApi.AdminRole}

jobId := c.Param("run_id")
controls := httpserver2.QueryArrayParam(c, "controls")
withIncidentsStr := c.QueryParam("with_incidents")

withIncidents := false
if withIncidentsStr == "true" {
withIncidents = true
complianceJob, err := h.schedulerClient.GetComplianceJobStatus(clientCtx, jobId)
if err != nil {
h.logger.Error("failed to get compliance job", zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get compliance job")
}
complianceJob, err := h.schedulerClient.GetComplianceJobStatus(clientCtx, jobId)
if err != nil {
h.logger.Error("failed to get compliance job", zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get compliance job")
}
if complianceJob.JobStatus == string(schedulerapi.ComplianceJobFailed) {
return echo.NewHTTPError(http.StatusBadRequest, "job has been failed")
} else if complianceJob.JobStatus == string(schedulerapi.ComplianceJobTimeout) {
return echo.NewHTTPError(http.StatusBadRequest, "job has been timed out")
} else if complianceJob.JobStatus == string(schedulerapi.ComplianceJobRunnersInProgress) ||
complianceJob.JobStatus == string(schedulerapi.ComplianceJobCreated) ||
complianceJob.JobStatus == string(schedulerapi.ComplianceJobSummarizerInProgress) {
return echo.NewHTTPError(http.StatusBadRequest, "job is in progress")
}
if complianceJob.WithIncidents {
if complianceJob.SummaryJobId == nil {
return echo.NewHTTPError(http.StatusBadRequest, "compliance job not summarized yet")
}
jobId = strconv.Itoa(int(*complianceJob.SummaryJobId))
} else {
auditJob, err := h.schedulerClient.GetComplianceQuickRun(clientCtx, jobId)
if err != nil {
h.logger.Error("failed to get audit job", zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get audit job")
}
if auditJob.Status == schedulerapi.ComplianceJobFailed {
return echo.NewHTTPError(http.StatusBadRequest, "job has been failed")
} else if auditJob.Status == schedulerapi.ComplianceJobTimeout {
return echo.NewHTTPError(http.StatusBadRequest, "job has been timed out")
} else if auditJob.Status == schedulerapi.ComplianceJobRunnersInProgress || auditJob.Status == schedulerapi.ComplianceJobCreated || auditJob.Status == schedulerapi.ComplianceJobSummarizerInProgress {
return echo.NewHTTPError(http.StatusBadRequest, "job is in progress")
}
}

summary, err := es.GetJobReportControlSummaryByJobID(c.Request().Context(), h.logger, h.client,
jobId, withIncidents, controls)
jobId, complianceJob.WithIncidents, controls)
if err != nil {
h.logger.Error("failed to get job report control summary by job id", zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get job report control summary by job id")
}

return c.JSON(http.StatusOK, summary)
}

// GetJobReportSummary godoc
//
// @Summary Get Job Report Summary
// @Description Get Job Report Summary
// @Security BearerToken
// @Tags workspace
// @Accept json
// @Produce json
// @Param controls query []string false "List of controls to get results"
// @Param with_incidents query bool false "Whether the job was with incidents or not"
// @Param run_id path string true "compliance summary job id"
// @Success 200
// @Router /compliance/api/v3/job-report/:run_id/summary [get]
func (h HttpHandler) GetJobReportSummary(ctx echo.Context) error {
clientCtx := &httpclient.Context{UserRole: authApi.AdminRole}

jobId := ctx.Param("run_id")
controlsFilter := httpserver2.QueryArrayParam(ctx, "controls")

complianceJob, err := h.schedulerClient.GetComplianceJobStatus(clientCtx, jobId)
if err != nil {
h.logger.Error("failed to get compliance job", zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get compliance job")
}
if complianceJob.JobStatus == string(schedulerapi.ComplianceJobFailed) {
return echo.NewHTTPError(http.StatusBadRequest, "job has been failed")
} else if complianceJob.JobStatus == string(schedulerapi.ComplianceJobTimeout) {
return echo.NewHTTPError(http.StatusBadRequest, "job has been timed out")
} else if complianceJob.JobStatus == string(schedulerapi.ComplianceJobRunnersInProgress) ||
complianceJob.JobStatus == string(schedulerapi.ComplianceJobCreated) ||
complianceJob.JobStatus == string(schedulerapi.ComplianceJobSummarizerInProgress) {
return echo.NewHTTPError(http.StatusBadRequest, "job is in progress")
}
if complianceJob.WithIncidents {
if complianceJob.SummaryJobId == nil {
return echo.NewHTTPError(http.StatusBadRequest, "compliance job not summarized yet")
}
jobId = strconv.Itoa(int(*complianceJob.SummaryJobId))
}

framework, err := h.db.GetBenchmark(ctx.Request().Context(), complianceJob.BenchmarkId)
if err != nil {
h.logger.Error("failed to get framework by frameworkID", zap.String("framework", complianceJob.BenchmarkId), zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get framework by frameworkID")
}

controlsMap, err := h.getControlsUnderBenchmark(ctx.Request().Context(), complianceJob.BenchmarkId, make(map[string]BenchmarkControlsCache))
if err != nil {
h.logger.Error("failed to get controls under benchmark", zap.String("framework", complianceJob.BenchmarkId), zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "could not get framework by frameworkID")
}
var controlsStr []string
for c, _ := range controlsMap {
controlsStr = append(controlsStr, c)
}
controls, err := h.db.ListControls(ctx.Request().Context(), controlsStr, nil)

summary, err := es.GetJobReportControlSummaryByJobID(ctx.Request().Context(), h.logger, h.client,
jobId, complianceJob.WithIncidents, controlsFilter)
if err != nil {
h.logger.Error("failed to get job report control summary by job id", zap.Error(err))
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get job report control summary by job id")
}

response := api.GetJobReportSummaryResponse{
JobID: complianceJob.JobId,
WithIncidents: complianceJob.WithIncidents,
JobDetails: api.GetJobReportSummaryJobDetails{
Status: complianceJob.JobStatus,
CreatedAt: complianceJob.CreatedAt,
UpdatedAt: complianceJob.UpdatedAt,
Framework: struct {
ID string `json:"id"`
Title string `json:"title"`
}{
ID: framework.ID,
Title: framework.Title,
},
IntegrationIDs: []string{complianceJob.IntegrationInfo.IntegrationID},
Results: api.GetJobReportSummaryJobDetailsResults{
Ok: summary.ComplianceSummary[types2.ComplianceStatusOK],
Alarm: summary.ComplianceSummary[types2.ComplianceStatusALARM],
},
},
}
jobScore := api.JobScore{
TotalControls: summary.ControlScore.TotalControls,
FailedControls: summary.ControlScore.FailedControls,
ControlView: api.JobScoreControlView{
BySeverity: make(map[string]*api.JobScoreControlViewBySeverityScore),
},
}
for _, c := range controls {
if _, ok := jobScore.ControlView.BySeverity[c.Severity.String()]; !ok {
jobScore.ControlView.BySeverity[c.Severity.String()] = &api.JobScoreControlViewBySeverityScore{}
}
jobScore.ControlView.BySeverity[c.Severity.String()].TotalControls += 1
}

for _, cs := range summary.Controls {
if _, ok := jobScore.ControlView.BySeverity[cs.Severity.String()]; !ok {
jobScore.ControlView.BySeverity[cs.Severity.String()] = &api.JobScoreControlViewBySeverityScore{}
}
if cs.Alarms > 0 {
jobScore.ControlView.BySeverity[cs.Severity.String()].FailedControls += 1
}
}
response.JobDetails.JobScore = jobScore

return ctx.JSON(http.StatusOK, response)
}
1 change: 1 addition & 0 deletions services/describe/api/jobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ type GetDescribeJobStatusResponse struct {

type GetComplianceJobStatusResponse struct {
JobId uint `json:"job_id"`
WithIncidents bool `json:"with_incidents"`
SummaryJobId *uint `json:"summary_job_id"`
IntegrationInfo IntegrationInfo `json:"integration_info"`
JobStatus string `json:"job_status"`
Expand Down
1 change: 0 additions & 1 deletion services/describe/client/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ type SchedulerServiceClient interface {
ListComplianceJobsHistory(ctx *httpclient.Context, interval, triggerType, createdBy string, cursor, perPage int) (*api.ListComplianceJobsHistoryResponse, error)
GetSummaryJobs(ctx *httpclient.Context, jobIDs []string) ([]string, error)
GetIntegrationLastDiscoveryJob(ctx *httpclient.Context, request api.GetIntegrationLastDiscoveryJobRequest) (*model.DescribeIntegrationJob, error)
GetComplianceQuickRun(ctx *httpclient.Context, jobID string) (*api.ComplianceJob, error)
GetComplianceQuickSequence(ctx *httpclient.Context, jobID string) (*api.QuickScanSequence, error)
GetComplianceJobStatus(ctx *httpclient.Context, jobId string) (*api.GetComplianceJobStatusResponse, error)
}
Expand Down
Loading

0 comments on commit f435bc5

Please sign in to comment.