diff --git a/pkg/coredata/evidence.go b/pkg/coredata/evidence.go index 373bb779e..1eb8aa7ea 100644 --- a/pkg/coredata/evidence.go +++ b/pkg/coredata/evidence.go @@ -31,6 +31,7 @@ type ( ID gid.GID `db:"id"` TaskID gid.GID `db:"task_id"` State EvidenceState `db:"state"` + ReferenceID string `db:"reference_id"` Type EvidenceType `db:"type"` ObjectKey string `db:"object_key"` MimeType string `db:"mime_type"` @@ -54,7 +55,7 @@ func (e Evidence) CursorKey(orderBy EvidenceOrderField) page.CursorKey { panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } -func (e Evidence) Insert( +func (e Evidence) Upsert( ctx context.Context, conn pg.Conn, scope Scoper, @@ -65,6 +66,7 @@ INSERT INTO tenant_id, id, task_id, + reference_id, object_key, mime_type, size, @@ -80,6 +82,73 @@ VALUES ( @tenant_id, @evidence_id, @task_id, + @reference_id, + @object_key, + @mime_type, + @size, + @state, + @type, + @filename, + @url, + @description, + @created_at, + @updated_at +) +ON CONFLICT (task_id, reference_id) DO UPDATE SET + description = @description, + type = @type, + updated_at = @updated_at +WHERE evidences.state = 'REQUESTED'; +` + + args := pgx.StrictNamedArgs{ + "tenant_id": scope.GetTenantID(), + "evidence_id": e.ID, + "task_id": e.TaskID, + "reference_id": e.ReferenceID, + "object_key": e.ObjectKey, + "mime_type": e.MimeType, + "size": e.Size, + "filename": e.Filename, + "created_at": e.CreatedAt, + "updated_at": e.UpdatedAt, + "state": e.State, + "type": e.Type, + "url": e.URL, + "description": e.Description, + } + _, err := conn.Exec(ctx, q, args) + return err +} + +func (e Evidence) Insert( + ctx context.Context, + conn pg.Conn, + scope Scoper, +) error { + q := ` +INSERT INTO + evidences ( + tenant_id, + id, + task_id, + reference_id, + object_key, + mime_type, + size, + state, + type, + filename, + url, + description, + created_at, + updated_at + ) +VALUES ( + @tenant_id, + @evidence_id, + @task_id, + @reference_id, @object_key, @mime_type, @size, @@ -94,19 +163,20 @@ VALUES ( ` args := pgx.StrictNamedArgs{ - "tenant_id": scope.GetTenantID(), - "evidence_id": e.ID, - "task_id": e.TaskID, - "object_key": e.ObjectKey, - "mime_type": e.MimeType, - "size": e.Size, - "filename": e.Filename, - "created_at": e.CreatedAt, - "updated_at": e.UpdatedAt, - "state": e.State, - "type": e.Type, - "url": e.URL, - "description": e.Description, + "tenant_id": scope.GetTenantID(), + "evidence_id": e.ID, + "task_id": e.TaskID, + "reference_id": e.ReferenceID, + "object_key": e.ObjectKey, + "mime_type": e.MimeType, + "size": e.Size, + "filename": e.Filename, + "created_at": e.CreatedAt, + "updated_at": e.UpdatedAt, + "state": e.State, + "type": e.Type, + "url": e.URL, + "description": e.Description, } _, err := conn.Exec(ctx, q, args) return err @@ -122,6 +192,7 @@ func (e *Evidence) LoadByID( SELECT id, task_id, + reference_id, state, type, object_key, @@ -171,6 +242,7 @@ func (e *Evidences) LoadByTaskID( SELECT id, task_id, + reference_id, state, type, object_key, diff --git a/pkg/coredata/migrations/20250403T093800Z.sql b/pkg/coredata/migrations/20250403T093800Z.sql new file mode 100644 index 000000000..15eedde98 --- /dev/null +++ b/pkg/coredata/migrations/20250403T093800Z.sql @@ -0,0 +1,7 @@ +ALTER TABLE evidences ADD COLUMN reference_id TEXT; + +UPDATE evidences SET reference_id = 'custom-evidence-' || gen_random_uuid()::text WHERE reference_id IS NULL; + +ALTER TABLE evidences ALTER COLUMN reference_id SET NOT NULL; + +ALTER TABLE evidences ADD CONSTRAINT evidences_reference_id_key UNIQUE (task_id, reference_id); \ No newline at end of file diff --git a/pkg/probo/evidence_service.go b/pkg/probo/evidence_service.go index fd334c74f..6ed200a69 100644 --- a/pkg/probo/evidence_service.go +++ b/pkg/probo/evidence_service.go @@ -197,10 +197,16 @@ func (s EvidenceService) Create( return nil, fmt.Errorf("cannot create evidence global id: %w", err) } + referenceID, err := uuid.NewV4() + if err != nil { + return nil, fmt.Errorf("cannot generate reference id: %w", err) + } + evidence := &coredata.Evidence{ ID: evidenceID, TaskID: req.TaskID, State: coredata.EvidenceStateFulfilled, + ReferenceID: "custom-evidence-" + referenceID.String(), Type: req.Type, Filename: req.Name, URL: req.URL, diff --git a/pkg/probo/mitigation_service.go b/pkg/probo/mitigation_service.go index 033a10263..af89c1dc6 100644 --- a/pkg/probo/mitigation_service.go +++ b/pkg/probo/mitigation_service.go @@ -22,6 +22,7 @@ import ( "github.com/getprobo/probo/pkg/coredata" "github.com/getprobo/probo/pkg/gid" "github.com/getprobo/probo/pkg/page" + "go.gearno.de/crypto/uuid" "go.gearno.de/kit/pg" ) @@ -59,9 +60,14 @@ type ( Control string `json:"control"` } `json:"standards"` Tasks []struct { - Name string `json:"name"` - Description string `json:"description"` - ReferenceID string `json:"reference-id"` + Name string `json:"name"` + Description string `json:"description"` + ReferenceID string `json:"reference-id"` + RequestedEvidences []struct { + ReferenceID string `json:"reference-id"` + Type coredata.EvidenceType `json:"type"` + Name string `json:"name"` + } `json:"requested-evidences"` } `json:"tasks"` } `json:"mitigations"` } @@ -186,6 +192,28 @@ func (s MitigationService) Import( if err := task.Upsert(ctx, tx, s.svc.scope); err != nil { return fmt.Errorf("cannot upsert task: %w", err) } + + for k := range req.Mitigations[i].Tasks[j].RequestedEvidences { + evidenceID, err := gid.NewGID(organizationID.TenantID(), coredata.EvidenceEntityType) + if err != nil { + return fmt.Errorf("cannot create global id: %w", err) + } + + evidence := &coredata.Evidence{ + State: coredata.EvidenceStateRequested, + ID: evidenceID, + TaskID: taskID, + ReferenceID: req.Mitigations[i].Tasks[j].RequestedEvidences[k].ReferenceID, + Type: req.Mitigations[i].Tasks[j].RequestedEvidences[k].Type, + Description: req.Mitigations[i].Tasks[j].RequestedEvidences[k].Name, + CreatedAt: now, + UpdatedAt: now, + } + + if err := evidence.Upsert(ctx, tx, s.svc.scope); err != nil { + return fmt.Errorf("cannot upsert evidence: %w", err) + } + } } for _, standard := range req.Mitigations[i].Standards { @@ -318,12 +346,18 @@ func (s MitigationService) Create( return nil, fmt.Errorf("cannot create mitigation global id: %w", err) } + referenceID, err := uuid.NewV4() + if err != nil { + return nil, fmt.Errorf("cannot generate reference id: %w", err) + } + mitigation := &coredata.Mitigation{ ID: mitigationID, OrganizationID: req.OrganizationID, Name: req.Name, Description: req.Description, Category: req.Category, + ReferenceID: "custom-mitigation-" + referenceID.String(), State: coredata.MitigationStateNotStarted, Importance: req.Importance, CreatedAt: now, diff --git a/pkg/probo/task_service.go b/pkg/probo/task_service.go index 6229c568b..dd309605e 100644 --- a/pkg/probo/task_service.go +++ b/pkg/probo/task_service.go @@ -22,7 +22,7 @@ import ( "github.com/getprobo/probo/pkg/coredata" "github.com/getprobo/probo/pkg/gid" "github.com/getprobo/probo/pkg/page" - "github.com/getprobo/probo/pkg/slug" + "go.gearno.de/crypto/uuid" "go.gearno.de/kit/pg" ) @@ -58,6 +58,11 @@ func (s TaskService) Create( return nil, fmt.Errorf("cannot generate id: %w", err) } + referenceID, err := uuid.NewV4() + if err != nil { + return nil, fmt.Errorf("cannot generate reference id: %w", err) + } + task := &coredata.Task{ ID: taskID, MitigationID: req.MitigationID, @@ -66,7 +71,7 @@ func (s TaskService) Create( TimeEstimate: req.TimeEstimate, AssignedToID: req.AssignedToID, State: coredata.TaskStateTodo, - ReferenceID: slug.Make(req.Name), + ReferenceID: "custom-task-" + referenceID.String(), CreatedAt: now, UpdatedAt: now, }